@better-auth/stripe 1.5.6 → 1.6.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client.d.mts +3 -3
- package/dist/client.mjs +2 -4
- package/dist/{index-nU1KjBOz.d.mts → index-8IZIblUa.d.mts} +81 -252
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +4 -11
- package/dist/{error-codes-CCosYkXx.mjs → version-2GttfqFB.mjs} +4 -4
- package/package.json +10 -9
- package/dist/client.mjs.map +0 -1
- package/dist/error-codes-CCosYkXx.mjs.map +0 -1
- package/dist/index.mjs.map +0 -1
package/dist/client.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as stripe, s as StripePlan } from "./index-
|
|
1
|
+
import { n as stripe, s as StripePlan } from "./index-8IZIblUa.mjs";
|
|
2
2
|
import * as better_auth0 from "better-auth";
|
|
3
3
|
|
|
4
4
|
//#region src/error-codes.d.ts
|
|
@@ -33,6 +33,7 @@ declare const stripeClient: <O extends {
|
|
|
33
33
|
subscription: boolean;
|
|
34
34
|
}>(options?: O | undefined) => {
|
|
35
35
|
id: "stripe-client";
|
|
36
|
+
version: string;
|
|
36
37
|
$InferServerPlugin: ReturnType<typeof stripe<O["subscription"] extends true ? {
|
|
37
38
|
stripeClient: any;
|
|
38
39
|
stripeWebhookSecret: string;
|
|
@@ -75,5 +76,4 @@ declare const stripeClient: <O extends {
|
|
|
75
76
|
};
|
|
76
77
|
};
|
|
77
78
|
//#endregion
|
|
78
|
-
export { STRIPE_ERROR_CODES, stripeClient };
|
|
79
|
-
//# sourceMappingURL=client.d.mts.map
|
|
79
|
+
export { STRIPE_ERROR_CODES, stripeClient };
|
package/dist/client.mjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { n as STRIPE_ERROR_CODES, t as PACKAGE_VERSION } from "./version-2GttfqFB.mjs";
|
|
3
2
|
//#region src/client.ts
|
|
4
3
|
const stripeClient = (options) => {
|
|
5
4
|
return {
|
|
6
5
|
id: "stripe-client",
|
|
6
|
+
version: PACKAGE_VERSION,
|
|
7
7
|
$InferServerPlugin: {},
|
|
8
8
|
pathMethods: {
|
|
9
9
|
"/subscription/billing-portal": "POST",
|
|
@@ -12,7 +12,5 @@ const stripeClient = (options) => {
|
|
|
12
12
|
$ERROR_CODES: STRIPE_ERROR_CODES
|
|
13
13
|
};
|
|
14
14
|
};
|
|
15
|
-
|
|
16
15
|
//#endregion
|
|
17
16
|
export { STRIPE_ERROR_CODES, stripeClient };
|
|
18
|
-
//# sourceMappingURL=client.mjs.map
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import * as better_auth0 from "better-auth";
|
|
2
2
|
import { GenericEndpointContext, InferOptionSchema, Session, User } from "better-auth";
|
|
3
|
-
import * as zod from "zod";
|
|
4
3
|
import * as better_call0 from "better-call";
|
|
5
4
|
import { Organization } from "better-auth/plugins/organization";
|
|
6
5
|
import Stripe from "stripe";
|
|
@@ -501,76 +500,36 @@ declare module "@better-auth/core" {
|
|
|
501
500
|
}
|
|
502
501
|
declare const stripe: <O extends StripeOptions>(options: O) => {
|
|
503
502
|
id: "stripe";
|
|
503
|
+
version: string;
|
|
504
504
|
endpoints: {
|
|
505
|
-
stripeWebhook: better_call0.
|
|
506
|
-
method: "POST";
|
|
507
|
-
metadata: {
|
|
508
|
-
openapi: {
|
|
509
|
-
operationId: string;
|
|
510
|
-
};
|
|
511
|
-
scope: "server";
|
|
512
|
-
};
|
|
513
|
-
cloneRequest: true;
|
|
514
|
-
disableBody: true;
|
|
515
|
-
}, {
|
|
505
|
+
stripeWebhook: better_call0.Endpoint<"/stripe/webhook", "POST", undefined, Record<string, any> | undefined, [], {
|
|
516
506
|
success: boolean;
|
|
517
|
-
}
|
|
507
|
+
}, {
|
|
508
|
+
openapi: {
|
|
509
|
+
operationId: string;
|
|
510
|
+
};
|
|
511
|
+
scope: "server";
|
|
512
|
+
}, undefined>;
|
|
518
513
|
} & (O["subscription"] extends {
|
|
519
514
|
enabled: true;
|
|
520
515
|
} ? {
|
|
521
|
-
upgradeSubscription: better_call0.
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
scheduleAtPeriodEnd: zod.ZodDefault<zod.ZodBoolean>;
|
|
539
|
-
disableRedirect: zod.ZodDefault<zod.ZodBoolean>;
|
|
540
|
-
}, better_auth0.$strip>;
|
|
541
|
-
metadata: {
|
|
542
|
-
openapi: {
|
|
543
|
-
operationId: string;
|
|
544
|
-
};
|
|
545
|
-
};
|
|
546
|
-
use: (((inputContext: better_call0.MiddlewareInputContext<{
|
|
547
|
-
use: ((inputContext: better_call0.MiddlewareInputContext<better_call0.MiddlewareOptions>) => Promise<{
|
|
548
|
-
session: {
|
|
549
|
-
session: Record<string, any> & {
|
|
550
|
-
id: string;
|
|
551
|
-
createdAt: Date;
|
|
552
|
-
updatedAt: Date;
|
|
553
|
-
userId: string;
|
|
554
|
-
expiresAt: Date;
|
|
555
|
-
token: string;
|
|
556
|
-
ipAddress?: string | null | undefined;
|
|
557
|
-
userAgent?: string | null | undefined;
|
|
558
|
-
};
|
|
559
|
-
user: Record<string, any> & {
|
|
560
|
-
id: string;
|
|
561
|
-
createdAt: Date;
|
|
562
|
-
updatedAt: Date;
|
|
563
|
-
email: string;
|
|
564
|
-
emailVerified: boolean;
|
|
565
|
-
name: string;
|
|
566
|
-
image?: string | null | undefined;
|
|
567
|
-
};
|
|
568
|
-
};
|
|
569
|
-
}>)[];
|
|
570
|
-
}>) => Promise<{
|
|
571
|
-
session: StripeCtxSession;
|
|
572
|
-
}>) | ((inputContext: better_call0.MiddlewareInputContext<better_call0.MiddlewareOptions>) => Promise<void>))[];
|
|
573
|
-
}, {
|
|
516
|
+
upgradeSubscription: better_call0.Endpoint<"/subscription/upgrade", "POST", {
|
|
517
|
+
plan: string;
|
|
518
|
+
annual?: boolean | undefined;
|
|
519
|
+
referenceId?: string | undefined;
|
|
520
|
+
subscriptionId?: string | undefined;
|
|
521
|
+
customerType?: "user" | "organization" | undefined;
|
|
522
|
+
metadata?: Record<string, any> | undefined;
|
|
523
|
+
seats?: number | undefined;
|
|
524
|
+
locale?: Stripe.Checkout.Session.Locale | undefined;
|
|
525
|
+
successUrl?: string | undefined;
|
|
526
|
+
cancelUrl?: string | undefined;
|
|
527
|
+
returnUrl?: string | undefined;
|
|
528
|
+
scheduleAtPeriodEnd?: boolean | undefined;
|
|
529
|
+
disableRedirect?: boolean | undefined;
|
|
530
|
+
}, Record<string, any> | undefined, [better_call0.Middleware<(inputContext: Record<string, any>) => Promise<{
|
|
531
|
+
session: StripeCtxSession;
|
|
532
|
+
}>>, better_call0.Middleware<(inputContext: Record<string, any>) => Promise<void>>, better_call0.Middleware<(inputContext: Record<string, any>) => Promise<void>>], {
|
|
574
533
|
url: string;
|
|
575
534
|
redirect: boolean;
|
|
576
535
|
} | {
|
|
@@ -651,140 +610,44 @@ declare const stripe: <O extends StripeOptions>(options: O) => {
|
|
|
651
610
|
idempotencyKey?: string;
|
|
652
611
|
stripeAccount?: string;
|
|
653
612
|
};
|
|
654
|
-
}>;
|
|
655
|
-
cancelSubscription: better_call0.StrictEndpoint<"/subscription/cancel", {
|
|
656
|
-
method: "POST";
|
|
657
|
-
body: zod.ZodObject<{
|
|
658
|
-
referenceId: zod.ZodOptional<zod.ZodString>;
|
|
659
|
-
subscriptionId: zod.ZodOptional<zod.ZodString>;
|
|
660
|
-
customerType: zod.ZodOptional<zod.ZodEnum<{
|
|
661
|
-
user: "user";
|
|
662
|
-
organization: "organization";
|
|
663
|
-
}>>;
|
|
664
|
-
returnUrl: zod.ZodString;
|
|
665
|
-
disableRedirect: zod.ZodDefault<zod.ZodBoolean>;
|
|
666
|
-
}, better_auth0.$strip>;
|
|
667
|
-
metadata: {
|
|
668
|
-
openapi: {
|
|
669
|
-
operationId: string;
|
|
670
|
-
};
|
|
671
|
-
};
|
|
672
|
-
use: (((inputContext: better_call0.MiddlewareInputContext<{
|
|
673
|
-
use: ((inputContext: better_call0.MiddlewareInputContext<better_call0.MiddlewareOptions>) => Promise<{
|
|
674
|
-
session: {
|
|
675
|
-
session: Record<string, any> & {
|
|
676
|
-
id: string;
|
|
677
|
-
createdAt: Date;
|
|
678
|
-
updatedAt: Date;
|
|
679
|
-
userId: string;
|
|
680
|
-
expiresAt: Date;
|
|
681
|
-
token: string;
|
|
682
|
-
ipAddress?: string | null | undefined;
|
|
683
|
-
userAgent?: string | null | undefined;
|
|
684
|
-
};
|
|
685
|
-
user: Record<string, any> & {
|
|
686
|
-
id: string;
|
|
687
|
-
createdAt: Date;
|
|
688
|
-
updatedAt: Date;
|
|
689
|
-
email: string;
|
|
690
|
-
emailVerified: boolean;
|
|
691
|
-
name: string;
|
|
692
|
-
image?: string | null | undefined;
|
|
693
|
-
};
|
|
694
|
-
};
|
|
695
|
-
}>)[];
|
|
696
|
-
}>) => Promise<{
|
|
697
|
-
session: StripeCtxSession;
|
|
698
|
-
}>) | ((inputContext: better_call0.MiddlewareInputContext<better_call0.MiddlewareOptions>) => Promise<void>))[];
|
|
699
613
|
}, {
|
|
614
|
+
openapi: {
|
|
615
|
+
operationId: string;
|
|
616
|
+
};
|
|
617
|
+
}, undefined>;
|
|
618
|
+
cancelSubscription: better_call0.Endpoint<"/subscription/cancel", "POST", {
|
|
619
|
+
returnUrl: string;
|
|
620
|
+
referenceId?: string | undefined;
|
|
621
|
+
subscriptionId?: string | undefined;
|
|
622
|
+
customerType?: "user" | "organization" | undefined;
|
|
623
|
+
disableRedirect?: boolean | undefined;
|
|
624
|
+
}, Record<string, any> | undefined, [better_call0.Middleware<(inputContext: Record<string, any>) => Promise<{
|
|
625
|
+
session: StripeCtxSession;
|
|
626
|
+
}>>, better_call0.Middleware<(inputContext: Record<string, any>) => Promise<void>>, better_call0.Middleware<(inputContext: Record<string, any>) => Promise<void>>], {
|
|
700
627
|
url: string;
|
|
701
628
|
redirect: boolean;
|
|
702
|
-
}
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
body: zod.ZodObject<{
|
|
706
|
-
referenceId: zod.ZodOptional<zod.ZodString>;
|
|
707
|
-
subscriptionId: zod.ZodOptional<zod.ZodString>;
|
|
708
|
-
customerType: zod.ZodOptional<zod.ZodEnum<{
|
|
709
|
-
user: "user";
|
|
710
|
-
organization: "organization";
|
|
711
|
-
}>>;
|
|
712
|
-
}, better_auth0.$strip>;
|
|
713
|
-
metadata: {
|
|
714
|
-
openapi: {
|
|
715
|
-
operationId: string;
|
|
716
|
-
};
|
|
629
|
+
}, {
|
|
630
|
+
openapi: {
|
|
631
|
+
operationId: string;
|
|
717
632
|
};
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
ipAddress?: string | null | undefined;
|
|
729
|
-
userAgent?: string | null | undefined;
|
|
730
|
-
};
|
|
731
|
-
user: Record<string, any> & {
|
|
732
|
-
id: string;
|
|
733
|
-
createdAt: Date;
|
|
734
|
-
updatedAt: Date;
|
|
735
|
-
email: string;
|
|
736
|
-
emailVerified: boolean;
|
|
737
|
-
name: string;
|
|
738
|
-
image?: string | null | undefined;
|
|
739
|
-
};
|
|
740
|
-
};
|
|
741
|
-
}>)[];
|
|
742
|
-
}>) => Promise<{
|
|
743
|
-
session: StripeCtxSession;
|
|
744
|
-
}>) | ((inputContext: better_call0.MiddlewareInputContext<better_call0.MiddlewareOptions>) => Promise<void>))[];
|
|
745
|
-
}, Stripe.Response<Stripe.Subscription>>;
|
|
746
|
-
listActiveSubscriptions: better_call0.StrictEndpoint<"/subscription/list", {
|
|
747
|
-
method: "GET";
|
|
748
|
-
query: zod.ZodOptional<zod.ZodObject<{
|
|
749
|
-
referenceId: zod.ZodOptional<zod.ZodString>;
|
|
750
|
-
customerType: zod.ZodOptional<zod.ZodEnum<{
|
|
751
|
-
user: "user";
|
|
752
|
-
organization: "organization";
|
|
753
|
-
}>>;
|
|
754
|
-
}, better_auth0.$strip>>;
|
|
755
|
-
metadata: {
|
|
756
|
-
openapi: {
|
|
757
|
-
operationId: string;
|
|
758
|
-
};
|
|
633
|
+
}, undefined>;
|
|
634
|
+
restoreSubscription: better_call0.Endpoint<"/subscription/restore", "POST", {
|
|
635
|
+
referenceId?: string | undefined;
|
|
636
|
+
subscriptionId?: string | undefined;
|
|
637
|
+
customerType?: "user" | "organization" | undefined;
|
|
638
|
+
}, Record<string, any> | undefined, [better_call0.Middleware<(inputContext: Record<string, any>) => Promise<{
|
|
639
|
+
session: StripeCtxSession;
|
|
640
|
+
}>>, better_call0.Middleware<(inputContext: Record<string, any>) => Promise<void>>], Stripe.Response<Stripe.Subscription>, {
|
|
641
|
+
openapi: {
|
|
642
|
+
operationId: string;
|
|
759
643
|
};
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
userId: string;
|
|
768
|
-
expiresAt: Date;
|
|
769
|
-
token: string;
|
|
770
|
-
ipAddress?: string | null | undefined;
|
|
771
|
-
userAgent?: string | null | undefined;
|
|
772
|
-
};
|
|
773
|
-
user: Record<string, any> & {
|
|
774
|
-
id: string;
|
|
775
|
-
createdAt: Date;
|
|
776
|
-
updatedAt: Date;
|
|
777
|
-
email: string;
|
|
778
|
-
emailVerified: boolean;
|
|
779
|
-
name: string;
|
|
780
|
-
image?: string | null | undefined;
|
|
781
|
-
};
|
|
782
|
-
};
|
|
783
|
-
}>)[];
|
|
784
|
-
}>) => Promise<{
|
|
785
|
-
session: StripeCtxSession;
|
|
786
|
-
}>) | ((inputContext: better_call0.MiddlewareInputContext<better_call0.MiddlewareOptions>) => Promise<void>))[];
|
|
787
|
-
}, {
|
|
644
|
+
}, undefined>;
|
|
645
|
+
listActiveSubscriptions: better_call0.Endpoint<"/subscription/list", "GET", undefined, {
|
|
646
|
+
referenceId?: string | undefined;
|
|
647
|
+
customerType?: "user" | "organization" | undefined;
|
|
648
|
+
} | undefined, [better_call0.Middleware<(inputContext: Record<string, any>) => Promise<{
|
|
649
|
+
session: StripeCtxSession;
|
|
650
|
+
}>>, better_call0.Middleware<(inputContext: Record<string, any>) => Promise<void>>], {
|
|
788
651
|
limits: Record<string, unknown> | undefined;
|
|
789
652
|
priceId: string | undefined;
|
|
790
653
|
id: string;
|
|
@@ -805,65 +668,32 @@ declare const stripe: <O extends StripeOptions>(options: O) => {
|
|
|
805
668
|
seats?: number | undefined;
|
|
806
669
|
billingInterval?: "day" | "week" | "month" | "year" | undefined;
|
|
807
670
|
stripeScheduleId?: string | undefined;
|
|
808
|
-
}[]
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
query: zod.ZodOptional<zod.ZodRecord<zod.ZodString, zod.ZodAny>>;
|
|
812
|
-
metadata: {
|
|
813
|
-
openapi: {
|
|
814
|
-
operationId: string;
|
|
815
|
-
};
|
|
671
|
+
}[], {
|
|
672
|
+
openapi: {
|
|
673
|
+
operationId: string;
|
|
816
674
|
};
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
body: zod.ZodObject<{
|
|
822
|
-
locale: zod.ZodOptional<zod.ZodCustom<Stripe.Checkout.Session.Locale, Stripe.Checkout.Session.Locale>>;
|
|
823
|
-
referenceId: zod.ZodOptional<zod.ZodString>;
|
|
824
|
-
customerType: zod.ZodOptional<zod.ZodEnum<{
|
|
825
|
-
user: "user";
|
|
826
|
-
organization: "organization";
|
|
827
|
-
}>>;
|
|
828
|
-
returnUrl: zod.ZodDefault<zod.ZodString>;
|
|
829
|
-
disableRedirect: zod.ZodDefault<zod.ZodBoolean>;
|
|
830
|
-
}, better_auth0.$strip>;
|
|
831
|
-
metadata: {
|
|
832
|
-
openapi: {
|
|
833
|
-
operationId: string;
|
|
834
|
-
};
|
|
675
|
+
}, undefined>;
|
|
676
|
+
subscriptionSuccess: better_call0.Endpoint<"/subscription/success", "GET", undefined, Record<string, any> | undefined, [better_call0.Middleware<(inputContext: Record<string, any>) => Promise<void>>], never, {
|
|
677
|
+
openapi: {
|
|
678
|
+
operationId: string;
|
|
835
679
|
};
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
ipAddress?: string | null | undefined;
|
|
847
|
-
userAgent?: string | null | undefined;
|
|
848
|
-
};
|
|
849
|
-
user: Record<string, any> & {
|
|
850
|
-
id: string;
|
|
851
|
-
createdAt: Date;
|
|
852
|
-
updatedAt: Date;
|
|
853
|
-
email: string;
|
|
854
|
-
emailVerified: boolean;
|
|
855
|
-
name: string;
|
|
856
|
-
image?: string | null | undefined;
|
|
857
|
-
};
|
|
858
|
-
};
|
|
859
|
-
}>)[];
|
|
860
|
-
}>) => Promise<{
|
|
861
|
-
session: StripeCtxSession;
|
|
862
|
-
}>) | ((inputContext: better_call0.MiddlewareInputContext<better_call0.MiddlewareOptions>) => Promise<void>))[];
|
|
863
|
-
}, {
|
|
680
|
+
}, undefined>;
|
|
681
|
+
createBillingPortal: better_call0.Endpoint<"/subscription/billing-portal", "POST", {
|
|
682
|
+
locale?: Stripe.Checkout.Session.Locale | undefined;
|
|
683
|
+
referenceId?: string | undefined;
|
|
684
|
+
customerType?: "user" | "organization" | undefined;
|
|
685
|
+
returnUrl?: string | undefined;
|
|
686
|
+
disableRedirect?: boolean | undefined;
|
|
687
|
+
}, Record<string, any> | undefined, [better_call0.Middleware<(inputContext: Record<string, any>) => Promise<{
|
|
688
|
+
session: StripeCtxSession;
|
|
689
|
+
}>>, better_call0.Middleware<(inputContext: Record<string, any>) => Promise<void>>, better_call0.Middleware<(inputContext: Record<string, any>) => Promise<void>>], {
|
|
864
690
|
url: string;
|
|
865
691
|
redirect: boolean;
|
|
866
|
-
}
|
|
692
|
+
}, {
|
|
693
|
+
openapi: {
|
|
694
|
+
operationId: string;
|
|
695
|
+
};
|
|
696
|
+
}, undefined>;
|
|
867
697
|
} : {});
|
|
868
698
|
init(ctx: better_auth0.AuthContext): {
|
|
869
699
|
options: {
|
|
@@ -1001,5 +831,4 @@ declare const stripe: <O extends StripeOptions>(options: O) => {
|
|
|
1001
831
|
};
|
|
1002
832
|
type StripePlugin<O extends StripeOptions> = ReturnType<typeof stripe<O>>;
|
|
1003
833
|
//#endregion
|
|
1004
|
-
export { StripeCtxSession as a, Subscription as c, WithStripeCustomerId as d, CustomerType as i, SubscriptionOptions as l, stripe as n, StripeOptions as o, AuthorizeReferenceAction as r, StripePlan as s, StripePlugin as t, WithActiveOrganizationId as u };
|
|
1005
|
-
//# sourceMappingURL=index-nU1KjBOz.d.mts.map
|
|
834
|
+
export { StripeCtxSession as a, Subscription as c, WithStripeCustomerId as d, CustomerType as i, SubscriptionOptions as l, stripe as n, StripeOptions as o, AuthorizeReferenceAction as r, StripePlan as s, StripePlugin as t, WithActiveOrganizationId as u };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as StripeCtxSession, c as Subscription, d as WithStripeCustomerId, i as CustomerType, l as SubscriptionOptions, n as stripe, o as StripeOptions, r as AuthorizeReferenceAction, s as StripePlan, t as StripePlugin, u as WithActiveOrganizationId } from "./index-
|
|
1
|
+
import { a as StripeCtxSession, c as Subscription, d as WithStripeCustomerId, i as CustomerType, l as SubscriptionOptions, n as stripe, o as StripeOptions, r as AuthorizeReferenceAction, s as StripePlan, t as StripePlugin, u as WithActiveOrganizationId } from "./index-8IZIblUa.mjs";
|
|
2
2
|
export { AuthorizeReferenceAction, CustomerType, StripeCtxSession, StripeOptions, StripePlan, StripePlugin, Subscription, SubscriptionOptions, WithActiveOrganizationId, WithStripeCustomerId, stripe };
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { n as STRIPE_ERROR_CODES, t as PACKAGE_VERSION } from "./version-2GttfqFB.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";
|
|
@@ -6,7 +6,6 @@ import { APIError as APIError$1 } from "@better-auth/core/error";
|
|
|
6
6
|
import { getSessionFromCtx, originCheck, sessionMiddleware } from "better-auth/api";
|
|
7
7
|
import * as z from "zod";
|
|
8
8
|
import { mergeSchema } from "better-auth/db";
|
|
9
|
-
|
|
10
9
|
//#region src/metadata.ts
|
|
11
10
|
/**
|
|
12
11
|
* Customer metadata - set internal fields and extract typed fields.
|
|
@@ -48,7 +47,6 @@ const subscriptionMetadata = {
|
|
|
48
47
|
};
|
|
49
48
|
}
|
|
50
49
|
};
|
|
51
|
-
|
|
52
50
|
//#endregion
|
|
53
51
|
//#region src/utils.ts
|
|
54
52
|
async function getPlans(subscriptionOptions) {
|
|
@@ -120,7 +118,6 @@ async function resolvePlanItem(options, items) {
|
|
|
120
118
|
plan: void 0
|
|
121
119
|
} : void 0;
|
|
122
120
|
}
|
|
123
|
-
|
|
124
121
|
//#endregion
|
|
125
122
|
//#region src/hooks.ts
|
|
126
123
|
/**
|
|
@@ -425,7 +422,6 @@ async function onSubscriptionDeleted(ctx, options, event) {
|
|
|
425
422
|
ctx.context.logger.error(`Stripe webhook failed. Error: ${error}`);
|
|
426
423
|
}
|
|
427
424
|
}
|
|
428
|
-
|
|
429
425
|
//#endregion
|
|
430
426
|
//#region src/middleware.ts
|
|
431
427
|
const stripeSessionMiddleware = createAuthMiddleware({ use: [sessionMiddleware] }, async (ctx) => {
|
|
@@ -464,7 +460,6 @@ const referenceMiddleware = (subscriptionOptions, action) => createAuthMiddlewar
|
|
|
464
460
|
action
|
|
465
461
|
}, ctx)) throw APIError$1.from("UNAUTHORIZED", STRIPE_ERROR_CODES.UNAUTHORIZED);
|
|
466
462
|
});
|
|
467
|
-
|
|
468
463
|
//#endregion
|
|
469
464
|
//#region src/routes.ts
|
|
470
465
|
/**
|
|
@@ -1249,10 +1244,11 @@ const listActiveSubscriptions = (options) => {
|
|
|
1249
1244
|
if (!plans) return [];
|
|
1250
1245
|
const subs = subscriptions.map((sub) => {
|
|
1251
1246
|
const plan = plans.find((p) => p.name.toLowerCase() === sub.plan.toLowerCase());
|
|
1247
|
+
const priceId = sub.billingInterval === "year" ? plan?.annualDiscountPriceId ?? plan?.priceId : plan?.priceId;
|
|
1252
1248
|
return {
|
|
1253
1249
|
...sub,
|
|
1254
1250
|
limits: plan?.limits,
|
|
1255
|
-
priceId
|
|
1251
|
+
priceId
|
|
1256
1252
|
};
|
|
1257
1253
|
}).filter((sub) => isActiveOrTrialing(sub));
|
|
1258
1254
|
return ctx.json(subs);
|
|
@@ -1468,7 +1464,6 @@ const stripeWebhook = (options) => {
|
|
|
1468
1464
|
return ctx.json({ success: true });
|
|
1469
1465
|
});
|
|
1470
1466
|
};
|
|
1471
|
-
|
|
1472
1467
|
//#endregion
|
|
1473
1468
|
//#region src/schema.ts
|
|
1474
1469
|
const subscriptions = { subscription: { fields: {
|
|
@@ -1563,7 +1558,6 @@ const getSchema = (options) => {
|
|
|
1563
1558
|
}
|
|
1564
1559
|
return mergeSchema(baseSchema, options.schema);
|
|
1565
1560
|
};
|
|
1566
|
-
|
|
1567
1561
|
//#endregion
|
|
1568
1562
|
//#region src/index.ts
|
|
1569
1563
|
const stripe = (options) => {
|
|
@@ -1578,6 +1572,7 @@ const stripe = (options) => {
|
|
|
1578
1572
|
};
|
|
1579
1573
|
return {
|
|
1580
1574
|
id: "stripe",
|
|
1575
|
+
version: PACKAGE_VERSION,
|
|
1581
1576
|
endpoints: {
|
|
1582
1577
|
stripeWebhook: stripeWebhook(options),
|
|
1583
1578
|
...options.subscription?.enabled ? subscriptionEndpoints : {}
|
|
@@ -1796,7 +1791,5 @@ const stripe = (options) => {
|
|
|
1796
1791
|
$ERROR_CODES: STRIPE_ERROR_CODES
|
|
1797
1792
|
};
|
|
1798
1793
|
};
|
|
1799
|
-
|
|
1800
1794
|
//#endregion
|
|
1801
1795
|
export { stripe };
|
|
1802
|
-
//# sourceMappingURL=index.mjs.map
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { defineErrorCodes } from "@better-auth/core/utils/error-codes";
|
|
2
|
-
|
|
3
2
|
//#region src/error-codes.ts
|
|
4
3
|
const STRIPE_ERROR_CODES = defineErrorCodes({
|
|
5
4
|
UNAUTHORIZED: "Unauthorized access",
|
|
@@ -26,7 +25,8 @@ const STRIPE_ERROR_CODES = defineErrorCodes({
|
|
|
26
25
|
ORGANIZATION_HAS_ACTIVE_SUBSCRIPTION: "Cannot delete organization with active subscription",
|
|
27
26
|
ORGANIZATION_REFERENCE_ID_REQUIRED: "Reference ID is required. Provide referenceId or set activeOrganizationId in session"
|
|
28
27
|
});
|
|
29
|
-
|
|
30
28
|
//#endregion
|
|
31
|
-
|
|
32
|
-
|
|
29
|
+
//#region src/version.ts
|
|
30
|
+
const PACKAGE_VERSION = "1.6.0-beta.0";
|
|
31
|
+
//#endregion
|
|
32
|
+
export { STRIPE_ERROR_CODES as n, PACKAGE_VERSION as t };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@better-auth/stripe",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0-beta.0",
|
|
4
4
|
"description": "Stripe plugin for Better Auth",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
"publishConfig": {
|
|
22
22
|
"access": "public"
|
|
23
23
|
},
|
|
24
|
+
"sideEffects": false,
|
|
24
25
|
"files": [
|
|
25
26
|
"dist"
|
|
26
27
|
],
|
|
@@ -54,22 +55,22 @@
|
|
|
54
55
|
"zod": "^4.3.6"
|
|
55
56
|
},
|
|
56
57
|
"devDependencies": {
|
|
57
|
-
"better-call": "
|
|
58
|
+
"better-call": "2.0.3",
|
|
58
59
|
"stripe": "^20.4.0",
|
|
59
|
-
"tsdown": "0.21.
|
|
60
|
-
"
|
|
61
|
-
"better-auth": "1.
|
|
60
|
+
"tsdown": "0.21.1",
|
|
61
|
+
"better-auth": "1.6.0-beta.0",
|
|
62
|
+
"@better-auth/core": "1.6.0-beta.0"
|
|
62
63
|
},
|
|
63
64
|
"peerDependencies": {
|
|
64
|
-
"better-call": "
|
|
65
|
+
"better-call": "2.0.3",
|
|
65
66
|
"stripe": "^18 || ^19 || ^20",
|
|
66
|
-
"better-auth": "1.
|
|
67
|
-
"
|
|
67
|
+
"@better-auth/core": "^1.6.0-beta.0",
|
|
68
|
+
"better-auth": "^1.6.0-beta.0"
|
|
68
69
|
},
|
|
69
70
|
"scripts": {
|
|
70
71
|
"build": "tsdown",
|
|
71
72
|
"dev": "tsdown --watch",
|
|
72
|
-
"lint:package": "publint run --strict",
|
|
73
|
+
"lint:package": "publint run --strict --pack false",
|
|
73
74
|
"lint:types": "attw --profile esm-only --pack .",
|
|
74
75
|
"typecheck": "tsc --project tsconfig.json",
|
|
75
76
|
"test": "vitest",
|
package/dist/client.mjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"client.mjs","names":[],"sources":["../src/client.ts"],"sourcesContent":["import type { BetterAuthClientPlugin } from \"better-auth/client\";\nimport { STRIPE_ERROR_CODES } from \"./error-codes\";\nimport type { StripePlan, stripe } from \"./index\";\nexport const stripeClient = <\n\tO extends {\n\t\tsubscription: boolean;\n\t},\n>(\n\toptions?: O | undefined,\n) => {\n\treturn {\n\t\tid: \"stripe-client\",\n\t\t$InferServerPlugin: {} as ReturnType<\n\t\t\ttypeof stripe<\n\t\t\t\tO[\"subscription\"] extends true\n\t\t\t\t\t? {\n\t\t\t\t\t\t\tstripeClient: any;\n\t\t\t\t\t\t\tstripeWebhookSecret: string;\n\t\t\t\t\t\t\tsubscription: {\n\t\t\t\t\t\t\t\tenabled: true;\n\t\t\t\t\t\t\t\tplans: StripePlan[];\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\t\t\t\t\t: {\n\t\t\t\t\t\t\tstripeClient: any;\n\t\t\t\t\t\t\tstripeWebhookSecret: string;\n\t\t\t\t\t\t}\n\t\t\t>\n\t\t>,\n\t\tpathMethods: {\n\t\t\t\"/subscription/billing-portal\": \"POST\",\n\t\t\t\"/subscription/restore\": \"POST\",\n\t\t},\n\t\t$ERROR_CODES: STRIPE_ERROR_CODES,\n\t} satisfies BetterAuthClientPlugin;\n};\nexport * from \"./error-codes\";\n"],"mappings":";;;AAGA,MAAa,gBAKZ,YACI;AACJ,QAAO;EACN,IAAI;EACJ,oBAAoB,EAAE;EAiBtB,aAAa;GACZ,gCAAgC;GAChC,yBAAyB;GACzB;EACD,cAAc;EACd"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"error-codes-CCosYkXx.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\t/**\n\t * @deprecated Use `SUBSCRIPTION_NOT_PENDING_CHANGE` instead.\n\t */\n\tSUBSCRIPTION_NOT_SCHEDULED_FOR_CANCELLATION:\n\t\t\"Subscription is not scheduled for cancellation\",\n\tSUBSCRIPTION_NOT_PENDING_CHANGE:\n\t\t\"Subscription has no pending cancellation or scheduled plan change\",\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;CAIzB,6CACC;CACD,iCACC;CACD,wBAAwB;CACxB,uCACC;CACD,8BACC;CACD,sCACC;CACD,oCACC;CACD,CAAC"}
|
package/dist/index.mjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["APIError","APIError"],"sources":["../src/metadata.ts","../src/utils.ts","../src/hooks.ts","../src/middleware.ts","../src/routes.ts","../src/schema.ts","../src/index.ts"],"sourcesContent":["import { defu } from \"defu\";\nimport type Stripe from \"stripe\";\n\n/**\n * Internal metadata fields for Stripe Customer.\n */\ntype CustomerInternalMetadata =\n\t| { customerType: \"user\"; userId: string }\n\t| { customerType: \"organization\"; organizationId: string };\n\n/**\n * Internal metadata fields for Stripe Subscription/Checkout.\n */\ntype SubscriptionInternalMetadata = {\n\tuserId: string;\n\tsubscriptionId: string;\n\treferenceId: string;\n};\n\n/**\n * Customer metadata - set internal fields and extract typed fields.\n */\nexport const customerMetadata = {\n\t/**\n\t * Internal metadata keys for type-safe access.\n\t */\n\tkeys: {\n\t\tuserId: \"userId\",\n\t\torganizationId: \"organizationId\",\n\t\tcustomerType: \"customerType\",\n\t} as const,\n\n\t/**\n\t * Create metadata with internal fields that cannot be overridden by user metadata.\n\t * Uses `defu` which prioritizes the first argument.\n\t */\n\tset(\n\t\tinternalFields: CustomerInternalMetadata,\n\t\t...userMetadata: (Stripe.Emptyable<Stripe.MetadataParam> | undefined)[]\n\t): Stripe.MetadataParam {\n\t\treturn defu(internalFields, ...userMetadata.filter(Boolean));\n\t},\n\n\t/**\n\t * Extract internal fields from Stripe metadata.\n\t * Provides type-safe access to internal metadata keys.\n\t */\n\tget(metadata: Stripe.Metadata | null | undefined) {\n\t\treturn {\n\t\t\tuserId: metadata?.userId,\n\t\t\torganizationId: metadata?.organizationId,\n\t\t\tcustomerType: metadata?.customerType as\n\t\t\t\t| CustomerInternalMetadata[\"customerType\"]\n\t\t\t\t| undefined,\n\t\t};\n\t},\n};\n\n/**\n * Subscription/Checkout metadata - set internal fields and extract typed fields.\n */\nexport const subscriptionMetadata = {\n\t/**\n\t * Internal metadata keys for type-safe access.\n\t */\n\tkeys: {\n\t\tuserId: \"userId\",\n\t\tsubscriptionId: \"subscriptionId\",\n\t\treferenceId: \"referenceId\",\n\t} as const,\n\n\t/**\n\t * Create metadata with internal fields that cannot be overridden by user metadata.\n\t * Uses `defu` which prioritizes the first argument.\n\t */\n\tset(\n\t\tinternalFields: SubscriptionInternalMetadata,\n\t\t...userMetadata: (Stripe.Emptyable<Stripe.MetadataParam> | undefined)[]\n\t): Stripe.MetadataParam {\n\t\treturn defu(internalFields, ...userMetadata.filter(Boolean));\n\t},\n\n\t/**\n\t * Extract internal fields from Stripe metadata.\n\t * Provides type-safe access to internal metadata keys.\n\t */\n\tget(metadata: Stripe.Metadata | null | undefined) {\n\t\treturn {\n\t\t\tuserId: metadata?.userId,\n\t\t\tsubscriptionId: metadata?.subscriptionId,\n\t\t\treferenceId: metadata?.referenceId,\n\t\t};\n\t},\n};\n","import type Stripe from \"stripe\";\nimport type { StripeOptions, StripePlan, Subscription } from \"./types\";\n\nexport async function getPlans(\n\tsubscriptionOptions: StripeOptions[\"subscription\"],\n) {\n\tif (subscriptionOptions?.enabled) {\n\t\treturn typeof subscriptionOptions.plans === \"function\"\n\t\t\t? await subscriptionOptions.plans()\n\t\t\t: subscriptionOptions.plans;\n\t}\n\tthrow new Error(\"Subscriptions are not enabled in the Stripe options.\");\n}\n\nexport async function getPlanByName(options: StripeOptions, name: string) {\n\treturn await getPlans(options.subscription).then((res) =>\n\t\tres?.find((plan) => plan.name.toLowerCase() === name.toLowerCase()),\n\t);\n}\n\n/**\n * Checks if a subscription is in an available state (active or trialing)\n */\nexport function isActiveOrTrialing(\n\tsub: Subscription | Stripe.Subscription,\n): boolean {\n\treturn sub.status === \"active\" || sub.status === \"trialing\";\n}\n\n/**\n * Check if a subscription is scheduled to be canceled (DB subscription object)\n */\nexport function isPendingCancel(sub: Subscription): boolean {\n\treturn !!(sub.cancelAtPeriodEnd || sub.cancelAt);\n}\n\n/**\n * Check if a Stripe subscription is scheduled to be canceled (Stripe API response)\n */\nexport function isStripePendingCancel(stripeSub: Stripe.Subscription): boolean {\n\treturn !!(stripeSub.cancel_at_period_end || stripeSub.cancel_at);\n}\n\n/**\n * Escapes a value for use in Stripe search queries.\n * Stripe search query uses double quotes for string values,\n * and double quotes within the value need to be escaped with backslash.\n *\n * @see https://docs.stripe.com/search#search-query-language\n */\nexport function escapeStripeSearchValue(value: string): string {\n\treturn value.replace(/\"/g, '\\\\\"');\n}\n\n/**\n * Resolve the quantity for a subscription by checking the seat item first,\n * then falling back to the plan item's quantity.\n */\nexport function resolveQuantity(\n\titems: Stripe.SubscriptionItem[],\n\tplanItem: Stripe.SubscriptionItem,\n\tseatPriceId?: string,\n): number {\n\tif (seatPriceId) {\n\t\tconst seatItem = items.find((item) => item.price.id === seatPriceId);\n\t\tif (seatItem) return seatItem.quantity ?? 1;\n\t}\n\treturn planItem.quantity ?? 1;\n}\n\n/**\n * Resolve the plan-matching subscription item and its plan config\n * from a (possibly multi-item) Stripe subscription.\n *\n * - Iterates items to find one whose price matches a configured plan.\n * - For single-item subscriptions, returns the item even without a plan match.\n */\nexport async function resolvePlanItem(\n\toptions: StripeOptions,\n\titems: Stripe.SubscriptionItem[],\n): Promise<\n\t{ item: Stripe.SubscriptionItem; plan: StripePlan | undefined } | undefined\n> {\n\tconst first = items[0];\n\tif (!first) return undefined;\n\tconst plans = await getPlans(options.subscription);\n\tfor (const item of items) {\n\t\tconst plan = plans?.find(\n\t\t\t(p) =>\n\t\t\t\tp.priceId === item.price.id ||\n\t\t\t\tp.annualDiscountPriceId === item.price.id ||\n\t\t\t\t(item.price.lookup_key &&\n\t\t\t\t\t(p.lookupKey === item.price.lookup_key ||\n\t\t\t\t\t\tp.annualDiscountLookupKey === item.price.lookup_key)),\n\t\t);\n\t\tif (plan) return { item, plan };\n\t}\n\treturn items.length === 1 ? { item: first, plan: undefined } : undefined;\n}\n","import type { GenericEndpointContext } from \"@better-auth/core\";\nimport type { User } from \"@better-auth/core/db\";\nimport type { Organization } from \"better-auth/plugins/organization\";\nimport type Stripe from \"stripe\";\nimport { subscriptionMetadata } from \"./metadata\";\nimport type { CustomerType, StripeOptions, Subscription } from \"./types\";\nimport {\n\tisActiveOrTrialing,\n\tisPendingCancel,\n\tisStripePendingCancel,\n\tresolvePlanItem,\n\tresolveQuantity,\n} from \"./utils\";\n\n/**\n * Find organization or user by stripeCustomerId.\n * @internal\n */\nasync function findReferenceByStripeCustomerId(\n\tctx: GenericEndpointContext,\n\toptions: StripeOptions,\n\tstripeCustomerId: string,\n): Promise<{ customerType: CustomerType; referenceId: string } | null> {\n\tif (options.organization?.enabled) {\n\t\tconst org = await ctx.context.adapter.findOne<Organization>({\n\t\t\tmodel: \"organization\",\n\t\t\twhere: [{ field: \"stripeCustomerId\", value: stripeCustomerId }],\n\t\t});\n\t\tif (org) return { customerType: \"organization\", referenceId: org.id };\n\t}\n\n\tconst user = await ctx.context.adapter.findOne<User>({\n\t\tmodel: \"user\",\n\t\twhere: [{ field: \"stripeCustomerId\", value: stripeCustomerId }],\n\t});\n\tif (user) return { customerType: \"user\", referenceId: user.id };\n\n\treturn null;\n}\n\nexport async function onCheckoutSessionCompleted(\n\tctx: GenericEndpointContext,\n\toptions: StripeOptions,\n\tevent: Stripe.Event,\n) {\n\ttry {\n\t\tconst client = options.stripeClient;\n\t\tconst checkoutSession = event.data.object as Stripe.Checkout.Session;\n\t\tif (checkoutSession.mode === \"setup\" || !options.subscription?.enabled) {\n\t\t\treturn;\n\t\t}\n\t\tconst subscription = await client.subscriptions.retrieve(\n\t\t\tcheckoutSession.subscription as string,\n\t\t);\n\t\tconst resolved = await resolvePlanItem(options, subscription.items.data);\n\t\tif (!resolved) {\n\t\t\tctx.context.logger.warn(\n\t\t\t\t`Stripe webhook warning: Subscription ${subscription.id} has no items matching a configured plan`,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\tconst { item: subscriptionItem, plan } = resolved;\n\t\tif (plan) {\n\t\t\tconst checkoutMeta = subscriptionMetadata.get(checkoutSession?.metadata);\n\t\t\tconst referenceId =\n\t\t\t\tcheckoutSession?.client_reference_id || checkoutMeta.referenceId;\n\t\t\tconst { subscriptionId } = checkoutMeta;\n\t\t\tconst seats = resolveQuantity(\n\t\t\t\tsubscription.items.data,\n\t\t\t\tsubscriptionItem,\n\t\t\t\tplan.seatPriceId,\n\t\t\t);\n\t\t\tif (referenceId && subscriptionId) {\n\t\t\t\tconst trial =\n\t\t\t\t\tsubscription.trial_start && subscription.trial_end\n\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\ttrialStart: new Date(subscription.trial_start * 1000),\n\t\t\t\t\t\t\t\ttrialEnd: new Date(subscription.trial_end * 1000),\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t: {};\n\n\t\t\t\tlet dbSubscription = await ctx.context.adapter.update<Subscription>({\n\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\tupdate: {\n\t\t\t\t\t\t...trial,\n\t\t\t\t\t\tplan: plan.name.toLowerCase(),\n\t\t\t\t\t\tstatus: subscription.status,\n\t\t\t\t\t\tupdatedAt: new Date(),\n\t\t\t\t\t\tperiodStart: new Date(subscriptionItem.current_period_start * 1000),\n\t\t\t\t\t\tperiodEnd: new Date(subscriptionItem.current_period_end * 1000),\n\t\t\t\t\t\tstripeSubscriptionId: checkoutSession.subscription as string,\n\t\t\t\t\t\tcancelAtPeriodEnd: subscription.cancel_at_period_end,\n\t\t\t\t\t\tcancelAt: subscription.cancel_at\n\t\t\t\t\t\t\t? new Date(subscription.cancel_at * 1000)\n\t\t\t\t\t\t\t: null,\n\t\t\t\t\t\tcanceledAt: subscription.canceled_at\n\t\t\t\t\t\t\t? new Date(subscription.canceled_at * 1000)\n\t\t\t\t\t\t\t: null,\n\t\t\t\t\t\tendedAt: subscription.ended_at\n\t\t\t\t\t\t\t? new Date(subscription.ended_at * 1000)\n\t\t\t\t\t\t\t: null,\n\t\t\t\t\t\tseats: seats,\n\t\t\t\t\t\tbillingInterval: subscriptionItem.price.recurring?.interval,\n\t\t\t\t\t},\n\t\t\t\t\twhere: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfield: \"id\",\n\t\t\t\t\t\t\tvalue: subscriptionId,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t});\n\n\t\t\t\tif (trial.trialStart && plan.freeTrial?.onTrialStart) {\n\t\t\t\t\tawait plan.freeTrial.onTrialStart(dbSubscription as Subscription);\n\t\t\t\t}\n\n\t\t\t\tif (!dbSubscription) {\n\t\t\t\t\tdbSubscription = await ctx.context.adapter.findOne<Subscription>({\n\t\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\t\twhere: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tfield: \"id\",\n\t\t\t\t\t\t\t\tvalue: subscriptionId,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tawait options.subscription?.onSubscriptionComplete?.(\n\t\t\t\t\t{\n\t\t\t\t\t\tevent,\n\t\t\t\t\t\tsubscription: dbSubscription as Subscription,\n\t\t\t\t\t\tstripeSubscription: subscription,\n\t\t\t\t\t\tplan,\n\t\t\t\t\t},\n\t\t\t\t\tctx,\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t} catch (e: any) {\n\t\tctx.context.logger.error(`Stripe webhook failed. Error: ${e.message}`);\n\t}\n}\n\nexport async function onSubscriptionCreated(\n\tctx: GenericEndpointContext,\n\toptions: StripeOptions,\n\tevent: Stripe.Event,\n) {\n\ttry {\n\t\tif (!options.subscription?.enabled) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst subscriptionCreated = event.data.object as Stripe.Subscription;\n\t\tconst stripeCustomerId = subscriptionCreated.customer?.toString();\n\t\tif (!stripeCustomerId) {\n\t\t\tctx.context.logger.warn(\n\t\t\t\t`Stripe webhook warning: customer.subscription.created event received without customer ID`,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\t// Check if subscription already exists in database\n\t\tconst { subscriptionId } = subscriptionMetadata.get(\n\t\t\tsubscriptionCreated.metadata,\n\t\t);\n\t\tconst existingSubscription =\n\t\t\tawait ctx.context.adapter.findOne<Subscription>({\n\t\t\t\tmodel: \"subscription\",\n\t\t\t\twhere: subscriptionId\n\t\t\t\t\t? [{ field: \"id\", value: subscriptionId }]\n\t\t\t\t\t: [{ field: \"stripeSubscriptionId\", value: subscriptionCreated.id }], // Probably won't match since it's not set yet\n\t\t\t});\n\t\tif (existingSubscription) {\n\t\t\tctx.context.logger.info(\n\t\t\t\t`Stripe webhook: Subscription already exists in database (id: ${existingSubscription.id}), skipping creation`,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\t// Find reference\n\t\tconst reference = await findReferenceByStripeCustomerId(\n\t\t\tctx,\n\t\t\toptions,\n\t\t\tstripeCustomerId,\n\t\t);\n\t\tif (!reference) {\n\t\t\tctx.context.logger.warn(\n\t\t\t\t`Stripe webhook warning: No user or organization found with stripeCustomerId: ${stripeCustomerId}`,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\tconst { referenceId, customerType } = reference;\n\n\t\tconst resolved = await resolvePlanItem(\n\t\t\toptions,\n\t\t\tsubscriptionCreated.items.data,\n\t\t);\n\t\tif (!resolved) {\n\t\t\tctx.context.logger.warn(\n\t\t\t\t`Stripe webhook warning: Subscription ${subscriptionCreated.id} has no items matching a configured plan`,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\tconst { item: subscriptionItem, plan } = resolved;\n\t\tif (!plan) {\n\t\t\tctx.context.logger.warn(\n\t\t\t\t`Stripe webhook warning: No matching plan found for priceId: ${subscriptionItem.price.id}`,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\tconst seats = resolveQuantity(\n\t\t\tsubscriptionCreated.items.data,\n\t\t\tsubscriptionItem,\n\t\t\tplan.seatPriceId,\n\t\t);\n\t\tconst periodStart = new Date(subscriptionItem.current_period_start * 1000);\n\t\tconst periodEnd = new Date(subscriptionItem.current_period_end * 1000);\n\n\t\tconst trial =\n\t\t\tsubscriptionCreated.trial_start && subscriptionCreated.trial_end\n\t\t\t\t? {\n\t\t\t\t\t\ttrialStart: new Date(subscriptionCreated.trial_start * 1000),\n\t\t\t\t\t\ttrialEnd: new Date(subscriptionCreated.trial_end * 1000),\n\t\t\t\t\t}\n\t\t\t\t: {};\n\n\t\t// Create the subscription in the database\n\t\tconst newSubscription = await ctx.context.adapter.create<Subscription>({\n\t\t\tmodel: \"subscription\",\n\t\t\tdata: {\n\t\t\t\t...trial,\n\t\t\t\t...(plan.limits ? { limits: plan.limits } : {}),\n\t\t\t\treferenceId,\n\t\t\t\tstripeCustomerId,\n\t\t\t\tstripeSubscriptionId: subscriptionCreated.id,\n\t\t\t\tstatus: subscriptionCreated.status,\n\t\t\t\tplan: plan.name.toLowerCase(),\n\t\t\t\tperiodStart,\n\t\t\t\tperiodEnd,\n\t\t\t\tseats,\n\t\t\t\tbillingInterval: subscriptionItem.price.recurring?.interval,\n\t\t\t},\n\t\t});\n\n\t\tctx.context.logger.info(\n\t\t\t`Stripe webhook: Created subscription ${subscriptionCreated.id} for ${customerType} ${referenceId} from dashboard`,\n\t\t);\n\n\t\tawait options.subscription.onSubscriptionCreated?.({\n\t\t\tevent,\n\t\t\tsubscription: newSubscription,\n\t\t\tstripeSubscription: subscriptionCreated,\n\t\t\tplan,\n\t\t});\n\t} catch (error: any) {\n\t\tctx.context.logger.error(`Stripe webhook failed. Error: ${error}`);\n\t}\n}\n\nexport async function onSubscriptionUpdated(\n\tctx: GenericEndpointContext,\n\toptions: StripeOptions,\n\tevent: Stripe.Event,\n) {\n\ttry {\n\t\tif (!options.subscription?.enabled) {\n\t\t\treturn;\n\t\t}\n\t\tconst subscriptionUpdated = event.data.object as Stripe.Subscription;\n\t\tconst resolved = await resolvePlanItem(\n\t\t\toptions,\n\t\t\tsubscriptionUpdated.items.data,\n\t\t);\n\t\tif (!resolved) {\n\t\t\tctx.context.logger.warn(\n\t\t\t\t`Stripe webhook warning: Subscription ${subscriptionUpdated.id} has no items matching a configured plan`,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\tconst { item: subscriptionItem, plan } = resolved;\n\n\t\tconst { subscriptionId } = subscriptionMetadata.get(\n\t\t\tsubscriptionUpdated.metadata,\n\t\t);\n\t\tconst customerId = subscriptionUpdated.customer?.toString();\n\t\tlet subscription = await ctx.context.adapter.findOne<Subscription>({\n\t\t\tmodel: \"subscription\",\n\t\t\twhere: subscriptionId\n\t\t\t\t? [{ field: \"id\", value: subscriptionId }]\n\t\t\t\t: [{ field: \"stripeSubscriptionId\", value: subscriptionUpdated.id }],\n\t\t});\n\t\tif (!subscription) {\n\t\t\tconst subs = await ctx.context.adapter.findMany<Subscription>({\n\t\t\t\tmodel: \"subscription\",\n\t\t\t\twhere: [{ field: \"stripeCustomerId\", value: customerId }],\n\t\t\t});\n\t\t\tif (subs.length > 1) {\n\t\t\t\tconst activeSub = subs.find((sub: Subscription) =>\n\t\t\t\t\tisActiveOrTrialing(sub),\n\t\t\t\t);\n\t\t\t\tif (!activeSub) {\n\t\t\t\t\tctx.context.logger.warn(\n\t\t\t\t\t\t`Stripe webhook error: Multiple subscriptions found for customerId: ${customerId} and no active subscription is found`,\n\t\t\t\t\t);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tsubscription = activeSub;\n\t\t\t} else {\n\t\t\t\tsubscription = subs[0]!;\n\t\t\t}\n\t\t}\n\n\t\tconst seats = plan\n\t\t\t? resolveQuantity(\n\t\t\t\t\tsubscriptionUpdated.items.data,\n\t\t\t\t\tsubscriptionItem,\n\t\t\t\t\tplan.seatPriceId,\n\t\t\t\t)\n\t\t\t: subscriptionItem.quantity;\n\n\t\tconst trial =\n\t\t\tsubscriptionUpdated.trial_start && subscriptionUpdated.trial_end\n\t\t\t\t? {\n\t\t\t\t\t\ttrialStart: new Date(subscriptionUpdated.trial_start * 1000),\n\t\t\t\t\t\ttrialEnd: new Date(subscriptionUpdated.trial_end * 1000),\n\t\t\t\t\t}\n\t\t\t\t: {};\n\n\t\tconst updatedSubscription = await ctx.context.adapter.update<Subscription>({\n\t\t\tmodel: \"subscription\",\n\t\t\tupdate: {\n\t\t\t\t...trial,\n\t\t\t\t...(plan\n\t\t\t\t\t? {\n\t\t\t\t\t\t\tplan: plan.name.toLowerCase(),\n\t\t\t\t\t\t\tlimits: plan.limits,\n\t\t\t\t\t\t}\n\t\t\t\t\t: {}),\n\t\t\t\tupdatedAt: new Date(),\n\t\t\t\tstatus: subscriptionUpdated.status,\n\t\t\t\tperiodStart: new Date(subscriptionItem.current_period_start * 1000),\n\t\t\t\tperiodEnd: new Date(subscriptionItem.current_period_end * 1000),\n\t\t\t\tcancelAtPeriodEnd: subscriptionUpdated.cancel_at_period_end,\n\t\t\t\tcancelAt: subscriptionUpdated.cancel_at\n\t\t\t\t\t? new Date(subscriptionUpdated.cancel_at * 1000)\n\t\t\t\t\t: null,\n\t\t\t\tcanceledAt: subscriptionUpdated.canceled_at\n\t\t\t\t\t? new Date(subscriptionUpdated.canceled_at * 1000)\n\t\t\t\t\t: null,\n\t\t\t\tendedAt: subscriptionUpdated.ended_at\n\t\t\t\t\t? new Date(subscriptionUpdated.ended_at * 1000)\n\t\t\t\t\t: null,\n\t\t\t\tseats,\n\t\t\t\tstripeSubscriptionId: subscriptionUpdated.id,\n\t\t\t\tbillingInterval: subscriptionItem.price.recurring?.interval,\n\t\t\t\tstripeScheduleId: subscriptionUpdated.schedule\n\t\t\t\t\t? typeof subscriptionUpdated.schedule === \"string\"\n\t\t\t\t\t\t? subscriptionUpdated.schedule\n\t\t\t\t\t\t: subscriptionUpdated.schedule.id\n\t\t\t\t\t: null,\n\t\t\t},\n\t\t\twhere: [\n\t\t\t\t{\n\t\t\t\t\tfield: \"id\",\n\t\t\t\t\tvalue: subscription.id,\n\t\t\t\t},\n\t\t\t],\n\t\t});\n\t\tconst isNewCancellation =\n\t\t\tsubscriptionUpdated.status === \"active\" &&\n\t\t\tisStripePendingCancel(subscriptionUpdated) &&\n\t\t\t!isPendingCancel(subscription);\n\t\tif (isNewCancellation) {\n\t\t\tawait options.subscription.onSubscriptionCancel?.({\n\t\t\t\tsubscription,\n\t\t\t\tcancellationDetails:\n\t\t\t\t\tsubscriptionUpdated.cancellation_details || undefined,\n\t\t\t\tstripeSubscription: subscriptionUpdated,\n\t\t\t\tevent,\n\t\t\t});\n\t\t}\n\t\tawait options.subscription.onSubscriptionUpdate?.({\n\t\t\tevent,\n\t\t\tsubscription: updatedSubscription || subscription,\n\t\t});\n\t\tif (plan) {\n\t\t\tif (\n\t\t\t\tsubscriptionUpdated.status === \"active\" &&\n\t\t\t\tsubscription.status === \"trialing\" &&\n\t\t\t\tplan.freeTrial?.onTrialEnd\n\t\t\t) {\n\t\t\t\tawait plan.freeTrial.onTrialEnd({ subscription }, ctx);\n\t\t\t}\n\t\t\tif (\n\t\t\t\tsubscriptionUpdated.status === \"incomplete_expired\" &&\n\t\t\t\tsubscription.status === \"trialing\" &&\n\t\t\t\tplan.freeTrial?.onTrialExpired\n\t\t\t) {\n\t\t\t\tawait plan.freeTrial.onTrialExpired(subscription, ctx);\n\t\t\t}\n\t\t}\n\t} catch (error: any) {\n\t\tctx.context.logger.error(`Stripe webhook failed. Error: ${error}`);\n\t}\n}\n\nexport async function onSubscriptionDeleted(\n\tctx: GenericEndpointContext,\n\toptions: StripeOptions,\n\tevent: Stripe.Event,\n) {\n\tif (!options.subscription?.enabled) {\n\t\treturn;\n\t}\n\ttry {\n\t\tconst subscriptionDeleted = event.data.object as Stripe.Subscription;\n\t\tconst subscriptionId = subscriptionDeleted.id;\n\t\tconst subscription = await ctx.context.adapter.findOne<Subscription>({\n\t\t\tmodel: \"subscription\",\n\t\t\twhere: [\n\t\t\t\t{\n\t\t\t\t\tfield: \"stripeSubscriptionId\",\n\t\t\t\t\tvalue: subscriptionId,\n\t\t\t\t},\n\t\t\t],\n\t\t});\n\t\tif (subscription) {\n\t\t\tconst trial =\n\t\t\t\tsubscriptionDeleted.trial_start && subscriptionDeleted.trial_end\n\t\t\t\t\t? {\n\t\t\t\t\t\t\ttrialStart: new Date(subscriptionDeleted.trial_start * 1000),\n\t\t\t\t\t\t\ttrialEnd: new Date(subscriptionDeleted.trial_end * 1000),\n\t\t\t\t\t\t}\n\t\t\t\t\t: {};\n\t\t\tawait ctx.context.adapter.update({\n\t\t\t\tmodel: \"subscription\",\n\t\t\t\twhere: [\n\t\t\t\t\t{\n\t\t\t\t\t\tfield: \"id\",\n\t\t\t\t\t\tvalue: subscription.id,\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\tupdate: {\n\t\t\t\t\t...trial,\n\t\t\t\t\tstatus: \"canceled\",\n\t\t\t\t\tupdatedAt: new Date(),\n\t\t\t\t\tcancelAtPeriodEnd: subscriptionDeleted.cancel_at_period_end,\n\t\t\t\t\tcancelAt: subscriptionDeleted.cancel_at\n\t\t\t\t\t\t? new Date(subscriptionDeleted.cancel_at * 1000)\n\t\t\t\t\t\t: null,\n\t\t\t\t\tcanceledAt: subscriptionDeleted.canceled_at\n\t\t\t\t\t\t? new Date(subscriptionDeleted.canceled_at * 1000)\n\t\t\t\t\t\t: null,\n\t\t\t\t\tendedAt: subscriptionDeleted.ended_at\n\t\t\t\t\t\t? new Date(subscriptionDeleted.ended_at * 1000)\n\t\t\t\t\t\t: null,\n\t\t\t\t\tstripeScheduleId: null,\n\t\t\t\t},\n\t\t\t});\n\t\t\tawait options.subscription.onSubscriptionDeleted?.({\n\t\t\t\tevent,\n\t\t\t\tstripeSubscription: subscriptionDeleted,\n\t\t\t\tsubscription,\n\t\t\t});\n\t\t} else {\n\t\t\tctx.context.logger.warn(\n\t\t\t\t`Stripe webhook error: Subscription not found for subscriptionId: ${subscriptionId}`,\n\t\t\t);\n\t\t}\n\t} catch (error: any) {\n\t\tctx.context.logger.error(`Stripe webhook failed. Error: ${error}`);\n\t}\n}\n","import { createAuthMiddleware } from \"@better-auth/core/api\";\nimport { APIError } from \"@better-auth/core/error\";\nimport { sessionMiddleware } from \"better-auth/api\";\nimport { STRIPE_ERROR_CODES } from \"./error-codes\";\nimport type {\n\tAuthorizeReferenceAction,\n\tCustomerType,\n\tStripeCtxSession,\n\tSubscriptionOptions,\n} from \"./types\";\n\nexport const stripeSessionMiddleware = createAuthMiddleware(\n\t{\n\t\tuse: [sessionMiddleware],\n\t},\n\tasync (ctx) => {\n\t\tconst session = ctx.context.session as StripeCtxSession;\n\t\treturn {\n\t\t\tsession,\n\t\t};\n\t},\n);\n\nexport const referenceMiddleware = (\n\tsubscriptionOptions: SubscriptionOptions,\n\taction: AuthorizeReferenceAction,\n) =>\n\tcreateAuthMiddleware(async (ctx) => {\n\t\tconst ctxSession = ctx.context.session as StripeCtxSession;\n\t\tif (!ctxSession) {\n\t\t\tthrow APIError.from(\"UNAUTHORIZED\", STRIPE_ERROR_CODES.UNAUTHORIZED);\n\t\t}\n\n\t\tconst customerType: CustomerType =\n\t\t\tctx.body?.customerType || ctx.query?.customerType;\n\t\tconst explicitReferenceId = ctx.body?.referenceId || ctx.query?.referenceId;\n\n\t\tif (customerType === \"organization\") {\n\t\t\t// Organization subscriptions always require authorizeReference\n\t\t\tif (!subscriptionOptions.authorizeReference) {\n\t\t\t\tctx.context.logger.error(\n\t\t\t\t\t`Organization subscriptions require authorizeReference to be defined in your stripe plugin config.`,\n\t\t\t\t);\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.AUTHORIZE_REFERENCE_REQUIRED,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst referenceId =\n\t\t\t\texplicitReferenceId || ctxSession.session.activeOrganizationId;\n\t\t\tif (!referenceId) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.ORGANIZATION_REFERENCE_ID_REQUIRED,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst isAuthorized = await subscriptionOptions.authorizeReference(\n\t\t\t\t{\n\t\t\t\t\tuser: ctxSession.user,\n\t\t\t\t\tsession: ctxSession.session,\n\t\t\t\t\treferenceId,\n\t\t\t\t\taction,\n\t\t\t\t},\n\t\t\t\tctx,\n\t\t\t);\n\t\t\tif (!isAuthorized) {\n\t\t\t\tthrow APIError.from(\"UNAUTHORIZED\", STRIPE_ERROR_CODES.UNAUTHORIZED);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// User subscriptions - pass if no explicit referenceId\n\t\tif (!explicitReferenceId) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Pass if referenceId is user id\n\t\tif (explicitReferenceId === ctxSession.user.id) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (!subscriptionOptions.authorizeReference) {\n\t\t\tctx.context.logger.error(\n\t\t\t\t`Passing referenceId into a subscription action isn't allowed if subscription.authorizeReference isn't defined in your stripe plugin config.`,\n\t\t\t);\n\t\t\tthrow APIError.from(\n\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\tSTRIPE_ERROR_CODES.REFERENCE_ID_NOT_ALLOWED,\n\t\t\t);\n\t\t}\n\t\tconst isAuthorized = await subscriptionOptions.authorizeReference(\n\t\t\t{\n\t\t\t\tuser: ctxSession.user,\n\t\t\t\tsession: ctxSession.session,\n\t\t\t\treferenceId: explicitReferenceId,\n\t\t\t\taction,\n\t\t\t},\n\t\t\tctx,\n\t\t);\n\t\tif (!isAuthorized) {\n\t\t\tthrow APIError.from(\"UNAUTHORIZED\", STRIPE_ERROR_CODES.UNAUTHORIZED);\n\t\t}\n\t});\n","import { createAuthEndpoint } from \"@better-auth/core/api\";\nimport { APIError } from \"@better-auth/core/error\";\nimport type { GenericEndpointContext, User } from \"better-auth\";\nimport { HIDE_METADATA } from \"better-auth\";\nimport { getSessionFromCtx, originCheck } from \"better-auth/api\";\nimport type { Organization } from \"better-auth/plugins/organization\";\nimport { defu } from \"defu\";\nimport type Stripe from \"stripe\";\nimport type { Stripe as StripeType } from \"stripe\";\nimport * as z from \"zod\";\nimport { STRIPE_ERROR_CODES } from \"./error-codes\";\nimport {\n\tonCheckoutSessionCompleted,\n\tonSubscriptionCreated,\n\tonSubscriptionDeleted,\n\tonSubscriptionUpdated,\n} from \"./hooks\";\nimport { customerMetadata, subscriptionMetadata } from \"./metadata\";\nimport { referenceMiddleware, stripeSessionMiddleware } from \"./middleware\";\nimport type {\n\tCustomerType,\n\tStripeCtxSession,\n\tStripeOptions,\n\tSubscription,\n\tSubscriptionOptions,\n\tWithStripeCustomerId,\n} from \"./types\";\nimport {\n\tescapeStripeSearchValue,\n\tgetPlanByName,\n\tgetPlans,\n\tisActiveOrTrialing,\n\tisPendingCancel,\n\tresolvePlanItem,\n\tresolveQuantity,\n} from \"./utils\";\n\n/**\n * Converts a relative URL to an absolute URL using baseURL.\n * @internal\n */\nfunction getUrl(ctx: GenericEndpointContext, url: string) {\n\tif (/^[a-zA-Z][a-zA-Z0-9+\\-.]*:/.test(url)) {\n\t\treturn url;\n\t}\n\treturn `${ctx.context.options.baseURL}${\n\t\turl.startsWith(\"/\") ? url : `/${url}`\n\t}`;\n}\n\n/**\n * Resolves a Stripe price ID from a lookup key.\n * @internal\n */\nasync function resolvePriceIdFromLookupKey(\n\tstripeClient: Stripe,\n\tlookupKey: string,\n): Promise<string | undefined> {\n\tif (!lookupKey) return undefined;\n\tconst prices = await stripeClient.prices.list({\n\t\tlookup_keys: [lookupKey],\n\t\tactive: true,\n\t\tlimit: 1,\n\t});\n\treturn prices.data[0]?.id;\n}\n\n/**\n * Determines the reference ID based on customer type.\n * - `user` (default): uses userId\n * - `organization`: uses activeOrganizationId from session\n * @internal\n */\nfunction getReferenceId(\n\tctxSession: StripeCtxSession,\n\tcustomerType: CustomerType | undefined,\n\toptions: StripeOptions,\n): string {\n\tconst { user, session } = ctxSession;\n\tconst type = customerType || \"user\";\n\n\tif (type === \"organization\") {\n\t\tif (!options.organization?.enabled) {\n\t\t\tthrow APIError.from(\n\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\tSTRIPE_ERROR_CODES.ORGANIZATION_SUBSCRIPTION_NOT_ENABLED,\n\t\t\t);\n\t\t}\n\n\t\tif (!session.activeOrganizationId) {\n\t\t\tthrow APIError.from(\n\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\tSTRIPE_ERROR_CODES.ORGANIZATION_NOT_FOUND,\n\t\t\t);\n\t\t}\n\t\treturn session.activeOrganizationId;\n\t}\n\n\treturn user.id;\n}\n\nconst upgradeSubscriptionBodySchema = z.object({\n\t/**\n\t * The name of the plan to subscribe\n\t */\n\tplan: z.string().meta({\n\t\tdescription: 'The name of the plan to upgrade to. Eg: \"pro\"',\n\t}),\n\t/**\n\t * If annual plan should be applied.\n\t */\n\tannual: z\n\t\t.boolean()\n\t\t.meta({\n\t\t\tdescription: \"Whether to upgrade to an annual plan. Eg: true\",\n\t\t})\n\t\t.optional(),\n\t/**\n\t * Reference ID for the subscription based on customerType:\n\t * - `user`: defaults to `user.id`\n\t * - `organization`: defaults to `session.activeOrganizationId`\n\t */\n\treferenceId: z\n\t\t.string()\n\t\t.meta({\n\t\t\tdescription: 'Reference ID for the subscription. Eg: \"org_123\"',\n\t\t})\n\t\t.optional(),\n\t/**\n\t * The Stripe subscription ID to upgrade.\n\t * If provided and not found, it'll throw an error.\n\t */\n\tsubscriptionId: z\n\t\t.string()\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t'The Stripe subscription ID to upgrade. Eg: \"sub_1ABC2DEF3GHI4JKL\"',\n\t\t})\n\t\t.optional(),\n\t/**\n\t * Customer type for the subscription.\n\t * - `user`: User owns the subscription (default)\n\t * - `organization`: Organization owns the subscription (requires referenceId)\n\t */\n\tcustomerType: z\n\t\t.enum([\"user\", \"organization\"])\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t'Customer type for the subscription. Eg: \"user\" or \"organization\"',\n\t\t})\n\t\t.optional(),\n\t/**\n\t * Additional metadata to store with the subscription.\n\t */\n\tmetadata: z.record(z.string(), z.any()).optional(),\n\t/**\n\t * Number of seats for subscriptions.\n\t */\n\tseats: z\n\t\t.number()\n\t\t.meta({\n\t\t\tdescription: \"Number of seats to upgrade to (if applicable). Eg: 1\",\n\t\t})\n\t\t.optional(),\n\t/**\n\t * The IETF language tag of the locale Checkout is displayed in.\n\t * If not provided or set to `auto`, the browser's locale is used.\n\t */\n\tlocale: z\n\t\t.custom<StripeType.Checkout.Session.Locale>((localization) => {\n\t\t\treturn typeof localization === \"string\";\n\t\t})\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t\"The locale to display Checkout in. Eg: 'en', 'ko'. If not provided or set to `auto`, the browser's locale is used.\",\n\t\t})\n\t\t.optional(),\n\t/**\n\t * The URL to which Stripe should send customers when payment or setup is complete.\n\t */\n\tsuccessUrl: z\n\t\t.string()\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t'Callback URL to redirect back after successful subscription. Eg: \"https://example.com/success\"',\n\t\t})\n\t\t.default(\"/\"),\n\t/**\n\t * If set, checkout shows a back button and customers will be directed here if they cancel payment.\n\t */\n\tcancelUrl: z\n\t\t.string()\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t'If set, checkout shows a back button and customers will be directed here if they cancel payment. Eg: \"https://example.com/pricing\"',\n\t\t})\n\t\t.default(\"/\"),\n\t/**\n\t * The URL to return to from the Billing Portal (used when upgrading existing subscriptions)\n\t */\n\treturnUrl: z\n\t\t.string()\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t'URL to take customers to when they click on the billing portal’s link to return to your website. Eg: \"https://example.com/dashboard\"',\n\t\t})\n\t\t.optional(),\n\t/**\n\t * Schedule the plan change at the end of the current billing period instead of applying immediately.\n\t */\n\tscheduleAtPeriodEnd: z\n\t\t.boolean()\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t\"Schedule the plan change at the end of the current billing period instead of applying immediately.\",\n\t\t})\n\t\t.default(false),\n\t/**\n\t * Disable Redirect\n\t */\n\tdisableRedirect: z\n\t\t.boolean()\n\t\t.meta({\n\t\t\tdescription: \"Disable redirect after successful subscription. Eg: true\",\n\t\t})\n\t\t.default(false),\n});\n\n/**\n * ### Endpoint\n *\n * POST `/subscription/upgrade`\n *\n * ### API Methods\n *\n * **server:**\n * `auth.api.upgradeSubscription`\n *\n * **client:**\n * `authClient.subscription.upgrade`\n *\n * @see [Read our docs to learn more.](https://better-auth.com/docs/plugins/stripe#api-method-subscription-upgrade)\n */\nexport const upgradeSubscription = (options: StripeOptions) => {\n\tconst client = options.stripeClient;\n\tconst subscriptionOptions = options.subscription as SubscriptionOptions;\n\n\treturn createAuthEndpoint(\n\t\t\"/subscription/upgrade\",\n\t\t{\n\t\t\tmethod: \"POST\",\n\t\t\tbody: upgradeSubscriptionBodySchema,\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\toperationId: \"upgradeSubscription\",\n\t\t\t\t},\n\t\t\t},\n\t\t\tuse: [\n\t\t\t\tstripeSessionMiddleware,\n\t\t\t\treferenceMiddleware(subscriptionOptions, \"upgrade-subscription\"),\n\t\t\t\toriginCheck((c) => {\n\t\t\t\t\treturn [c.body.successUrl as string, c.body.cancelUrl as string];\n\t\t\t\t}),\n\t\t\t],\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tconst { user, session } = ctx.context.session;\n\t\t\tconst customerType = ctx.body.customerType || \"user\";\n\t\t\tconst referenceId =\n\t\t\t\tctx.body.referenceId ||\n\t\t\t\tgetReferenceId(ctx.context.session, customerType, options);\n\n\t\t\tif (!user.emailVerified && subscriptionOptions.requireEmailVerification) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.EMAIL_VERIFICATION_REQUIRED,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst plan = await getPlanByName(options, ctx.body.plan);\n\t\t\tif (!plan) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.SUBSCRIPTION_PLAN_NOT_FOUND,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// If subscriptionId is provided, find that specific subscription.\n\t\t\t// Otherwise, active subscription will be resolved by referenceId later.\n\t\t\tconst subscriptionToUpdate = ctx.body.subscriptionId\n\t\t\t\t? await ctx.context.adapter.findOne<Subscription>({\n\t\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\t\twhere: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tfield: \"stripeSubscriptionId\",\n\t\t\t\t\t\t\t\tvalue: ctx.body.subscriptionId,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t})\n\t\t\t\t: null;\n\t\t\tif (ctx.body.subscriptionId && !subscriptionToUpdate) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.SUBSCRIPTION_NOT_FOUND,\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (\n\t\t\t\tctx.body.subscriptionId &&\n\t\t\t\tsubscriptionToUpdate &&\n\t\t\t\tsubscriptionToUpdate.referenceId !== referenceId\n\t\t\t) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.SUBSCRIPTION_NOT_FOUND,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Determine customer id\n\t\t\tlet customerId: string | undefined;\n\t\t\tif (customerType === \"organization\") {\n\t\t\t\t// Organization subscription - get customer ID from organization\n\t\t\t\tcustomerId = subscriptionToUpdate?.stripeCustomerId;\n\t\t\t\tif (!customerId) {\n\t\t\t\t\tconst org = await ctx.context.adapter.findOne<\n\t\t\t\t\t\tOrganization & WithStripeCustomerId\n\t\t\t\t\t>({\n\t\t\t\t\t\tmodel: \"organization\",\n\t\t\t\t\t\twhere: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tfield: \"id\",\n\t\t\t\t\t\t\t\tvalue: referenceId,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t});\n\t\t\t\t\tif (!org) {\n\t\t\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\t\t\tSTRIPE_ERROR_CODES.ORGANIZATION_NOT_FOUND,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tcustomerId = org.stripeCustomerId;\n\n\t\t\t\t\t// If org doesn't have a customer ID, create one\n\t\t\t\t\tif (!customerId) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t// Find existing organization customer by organizationId metadata\n\t\t\t\t\t\t\tlet stripeCustomer: Stripe.Customer | undefined;\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tconst result = await client.customers.search({\n\t\t\t\t\t\t\t\t\tquery: `metadata[\"${customerMetadata.keys.organizationId}\"]:\"${org.id}\" AND metadata[\"${customerMetadata.keys.customerType}\"]:\"organization\"`,\n\t\t\t\t\t\t\t\t\tlimit: 1,\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\tstripeCustomer = result.data[0];\n\t\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t\t// Search API unavailable in some regions, so fall back to paginated list\n\t\t\t\t\t\t\t\tctx.context.logger.warn(\n\t\t\t\t\t\t\t\t\t\"Stripe customers.search failed, falling back to customers.list\",\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\tfor await (const customer of client.customers.list({\n\t\t\t\t\t\t\t\t\tlimit: 100,\n\t\t\t\t\t\t\t\t})) {\n\t\t\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t\t\tcustomer.metadata?.[\n\t\t\t\t\t\t\t\t\t\t\tcustomerMetadata.keys.organizationId\n\t\t\t\t\t\t\t\t\t\t] === org.id &&\n\t\t\t\t\t\t\t\t\t\tcustomer.metadata?.[customerMetadata.keys.customerType] ===\n\t\t\t\t\t\t\t\t\t\t\t\"organization\"\n\t\t\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\t\t\tstripeCustomer = customer;\n\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif (!stripeCustomer) {\n\t\t\t\t\t\t\t\t// Get custom params if provided\n\t\t\t\t\t\t\t\tlet extraCreateParams: Partial<StripeType.CustomerCreateParams> =\n\t\t\t\t\t\t\t\t\t{};\n\t\t\t\t\t\t\t\tif (options.organization?.getCustomerCreateParams) {\n\t\t\t\t\t\t\t\t\textraCreateParams =\n\t\t\t\t\t\t\t\t\t\tawait options.organization.getCustomerCreateParams(\n\t\t\t\t\t\t\t\t\t\t\torg,\n\t\t\t\t\t\t\t\t\t\t\tctx,\n\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// Create Stripe customer for organization\n\t\t\t\t\t\t\t\t// Email can be set via getCustomerCreateParams or updated in billing portal\n\t\t\t\t\t\t\t\t// Use defu to merge params (first argument takes priority)\n\t\t\t\t\t\t\t\tconst customerParams = defu(\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tname: org.name,\n\t\t\t\t\t\t\t\t\t\tmetadata: customerMetadata.set(\n\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\torganizationId: org.id,\n\t\t\t\t\t\t\t\t\t\t\t\tcustomerType: \"organization\",\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tctx.body.metadata,\n\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\textraCreateParams,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\tstripeCustomer = await client.customers.create(customerParams);\n\n\t\t\t\t\t\t\t\t// Call onCustomerCreate callback only for newly created customers\n\t\t\t\t\t\t\t\tawait options.organization?.onCustomerCreate?.(\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tstripeCustomer,\n\t\t\t\t\t\t\t\t\t\torganization: {\n\t\t\t\t\t\t\t\t\t\t\t...org,\n\t\t\t\t\t\t\t\t\t\t\tstripeCustomerId: stripeCustomer.id,\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\tctx,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tawait ctx.context.adapter.update({\n\t\t\t\t\t\t\t\tmodel: \"organization\",\n\t\t\t\t\t\t\t\tupdate: {\n\t\t\t\t\t\t\t\t\tstripeCustomerId: stripeCustomer.id,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\twhere: [\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tfield: \"id\",\n\t\t\t\t\t\t\t\t\t\tvalue: org.id,\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t});\n\n\t\t\t\t\t\t\tcustomerId = stripeCustomer.id;\n\t\t\t\t\t\t} catch (e: any) {\n\t\t\t\t\t\t\tctx.context.logger.error(e);\n\t\t\t\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\t\t\t\tSTRIPE_ERROR_CODES.UNABLE_TO_CREATE_CUSTOMER,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// User subscription - get customer ID from user\n\t\t\t\tcustomerId =\n\t\t\t\t\tsubscriptionToUpdate?.stripeCustomerId || user.stripeCustomerId;\n\t\t\t\tif (!customerId) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\t// Find existing user Stripe customer by email\n\t\t\t\t\t\tlet stripeCustomer: Stripe.Customer | undefined;\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tconst result = await client.customers.search({\n\t\t\t\t\t\t\t\tquery: `email:\"${escapeStripeSearchValue(user.email)}\" AND -metadata[\"${customerMetadata.keys.customerType}\"]:\"organization\"`,\n\t\t\t\t\t\t\t\tlimit: 1,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tstripeCustomer = result.data[0];\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t// Search API unavailable in some regions, so fall back to paginated list\n\t\t\t\t\t\t\tctx.context.logger.warn(\n\t\t\t\t\t\t\t\t\"Stripe customers.search failed, falling back to customers.list\",\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tfor await (const customer of client.customers.list({\n\t\t\t\t\t\t\t\temail: user.email,\n\t\t\t\t\t\t\t\tlimit: 100,\n\t\t\t\t\t\t\t})) {\n\t\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t\tcustomer.metadata?.[customerMetadata.keys.customerType] !==\n\t\t\t\t\t\t\t\t\t\"organization\"\n\t\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\t\tstripeCustomer = customer;\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (!stripeCustomer) {\n\t\t\t\t\t\t\tstripeCustomer = await client.customers.create({\n\t\t\t\t\t\t\t\temail: user.email,\n\t\t\t\t\t\t\t\tname: user.name,\n\t\t\t\t\t\t\t\tmetadata: customerMetadata.set(\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tuserId: user.id,\n\t\t\t\t\t\t\t\t\t\tcustomerType: \"user\",\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\tctx.body.metadata,\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Update local DB with Stripe customer ID\n\t\t\t\t\t\tawait ctx.context.adapter.update({\n\t\t\t\t\t\t\tmodel: \"user\",\n\t\t\t\t\t\t\tupdate: {\n\t\t\t\t\t\t\t\tstripeCustomerId: stripeCustomer.id,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\twhere: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tfield: \"id\",\n\t\t\t\t\t\t\t\t\tvalue: user.id,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\tcustomerId = stripeCustomer.id;\n\t\t\t\t\t} catch (e: any) {\n\t\t\t\t\t\tctx.context.logger.error(e);\n\t\t\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\t\t\tSTRIPE_ERROR_CODES.UNABLE_TO_CREATE_CUSTOMER,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst subscriptions = subscriptionToUpdate\n\t\t\t\t? [subscriptionToUpdate]\n\t\t\t\t: await ctx.context.adapter.findMany<Subscription>({\n\t\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\t\twhere: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tfield: \"referenceId\",\n\t\t\t\t\t\t\t\tvalue: referenceId,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t});\n\n\t\t\tconst activeOrTrialingSubscription = subscriptions.find((sub) =>\n\t\t\t\tisActiveOrTrialing(sub),\n\t\t\t);\n\n\t\t\tconst activeSubscriptions = await client.subscriptions\n\t\t\t\t.list({\n\t\t\t\t\tcustomer: customerId,\n\t\t\t\t})\n\t\t\t\t.then((res) => res.data.filter((sub) => isActiveOrTrialing(sub)));\n\n\t\t\tconst activeSubscription = activeSubscriptions.find((sub) => {\n\t\t\t\t// If we have a specific subscription to update, match by ID\n\t\t\t\tif (\n\t\t\t\t\tsubscriptionToUpdate?.stripeSubscriptionId ||\n\t\t\t\t\tctx.body.subscriptionId\n\t\t\t\t) {\n\t\t\t\t\treturn (\n\t\t\t\t\t\tsub.id === subscriptionToUpdate?.stripeSubscriptionId ||\n\t\t\t\t\t\tsub.id === ctx.body.subscriptionId\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\t// Only find subscription for the same referenceId to avoid mixing personal and org subscriptions\n\t\t\t\tif (activeOrTrialingSubscription?.stripeSubscriptionId) {\n\t\t\t\t\treturn sub.id === activeOrTrialingSubscription.stripeSubscriptionId;\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t});\n\n\t\t\t// Get the current price ID from the active Stripe subscription\n\t\t\tconst resolvedPlan = activeSubscription\n\t\t\t\t? await resolvePlanItem(options, activeSubscription.items.data)\n\t\t\t\t: undefined;\n\t\t\tconst planItem = resolvedPlan?.item;\n\t\t\tconst stripeSubscriptionPriceId = planItem?.price.id;\n\n\t\t\t// Also find any incomplete subscription that we can reuse\n\t\t\tconst incompleteSubscription = subscriptions.find(\n\t\t\t\t(sub) => sub.status === \"incomplete\",\n\t\t\t);\n\n\t\t\tconst priceId = ctx.body.annual\n\t\t\t\t? plan.annualDiscountPriceId\n\t\t\t\t: plan.priceId;\n\t\t\tconst lookupKey = ctx.body.annual\n\t\t\t\t? plan.annualDiscountLookupKey\n\t\t\t\t: plan.lookupKey;\n\t\t\tconst resolvedPriceId = lookupKey\n\t\t\t\t? await resolvePriceIdFromLookupKey(client, lookupKey)\n\t\t\t\t: undefined;\n\n\t\t\tconst priceIdToUse = priceId || resolvedPriceId;\n\t\t\tif (!priceIdToUse) {\n\t\t\t\tthrow ctx.error(\"BAD_REQUEST\", {\n\t\t\t\t\tmessage: \"Price ID not found for the selected plan\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// For org subscriptions with seat-based billing, seats are auto-managed.\n\t\t\t// Quantity = memberCount; use Stripe graduated pricing for free tiers.\n\t\t\tconst isAutoManagedSeats = !!(\n\t\t\t\tplan.seatPriceId && customerType === \"organization\"\n\t\t\t);\n\t\t\tlet memberCount = 0;\n\t\t\tif (isAutoManagedSeats) {\n\t\t\t\tmemberCount = await ctx.context.adapter.count({\n\t\t\t\t\tmodel: \"member\",\n\t\t\t\t\twhere: [{ field: \"organizationId\", value: referenceId }],\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst isSamePlan = activeOrTrialingSubscription?.plan === ctx.body.plan;\n\t\t\tconst isSameSeats = isAutoManagedSeats\n\t\t\t\t? true // seats are auto-managed, don't block upgrade\n\t\t\t\t: activeOrTrialingSubscription?.seats === (ctx.body.seats || 1);\n\t\t\tconst isSamePriceId = stripeSubscriptionPriceId === priceIdToUse;\n\t\t\tconst isSubscriptionStillValid =\n\t\t\t\t!activeOrTrialingSubscription?.periodEnd ||\n\t\t\t\tactiveOrTrialingSubscription.periodEnd > new Date();\n\t\t\tconst isSeatOnlyPlan =\n\t\t\t\tisAutoManagedSeats && plan.seatPriceId === plan.priceId;\n\n\t\t\tconst isAlreadySubscribed =\n\t\t\t\tactiveOrTrialingSubscription?.status === \"active\" &&\n\t\t\t\tisSamePlan &&\n\t\t\t\tisSameSeats &&\n\t\t\t\tisSamePriceId &&\n\t\t\t\tisSubscriptionStillValid;\n\t\t\tif (isAlreadySubscribed) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.ALREADY_SUBSCRIBED_PLAN,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tif (activeSubscription && customerId) {\n\t\t\t\t// Find the corresponding database subscription for this Stripe subscription\n\t\t\t\tlet dbSubscription = await ctx.context.adapter.findOne<Subscription>({\n\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\twhere: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfield: \"stripeSubscriptionId\",\n\t\t\t\t\t\t\tvalue: activeSubscription.id,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t});\n\n\t\t\t\t// If no database record exists for this Stripe subscription, update the existing one\n\t\t\t\tif (!dbSubscription && activeOrTrialingSubscription) {\n\t\t\t\t\tawait ctx.context.adapter.update<Subscription>({\n\t\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\t\tupdate: {\n\t\t\t\t\t\t\tstripeSubscriptionId: activeSubscription.id,\n\t\t\t\t\t\t\tupdatedAt: new Date(),\n\t\t\t\t\t\t},\n\t\t\t\t\t\twhere: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tfield: \"id\",\n\t\t\t\t\t\t\t\tvalue: activeOrTrialingSubscription.id,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t});\n\t\t\t\t\tdbSubscription = activeOrTrialingSubscription;\n\t\t\t\t}\n\n\t\t\t\tif (!planItem) {\n\t\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\t\"NOT_FOUND\",\n\t\t\t\t\t\tSTRIPE_ERROR_CODES.SUBSCRIPTION_NOT_FOUND,\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\t// Release any existing plugin-created subscription schedule.\n\t\t\t\t// Only check when a schedule is attached to avoid unnecessary API calls.\n\t\t\t\tif (activeSubscription.schedule) {\n\t\t\t\t\tconst { data: existingSchedules } =\n\t\t\t\t\t\tawait client.subscriptionSchedules.list({\n\t\t\t\t\t\t\tcustomer: customerId,\n\t\t\t\t\t\t});\n\t\t\t\t\tconst existingSchedule = existingSchedules.find(\n\t\t\t\t\t\t(s) =>\n\t\t\t\t\t\t\t(typeof s.subscription === \"string\"\n\t\t\t\t\t\t\t\t? s.subscription\n\t\t\t\t\t\t\t\t: s.subscription?.id) === activeSubscription.id &&\n\t\t\t\t\t\t\ts.status === \"active\",\n\t\t\t\t\t);\n\t\t\t\t\tif (\n\t\t\t\t\t\texistingSchedule &&\n\t\t\t\t\t\texistingSchedule.metadata?.source === \"@better-auth/stripe\"\n\t\t\t\t\t) {\n\t\t\t\t\t\tawait client.subscriptionSchedules.release(existingSchedule.id);\n\t\t\t\t\t\tif (dbSubscription) {\n\t\t\t\t\t\t\tawait ctx.context.adapter.update({\n\t\t\t\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\t\t\t\tupdate: {\n\t\t\t\t\t\t\t\t\tstripeScheduleId: null,\n\t\t\t\t\t\t\t\t\tupdatedAt: new Date(),\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\twhere: [\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tfield: \"id\",\n\t\t\t\t\t\t\t\t\t\tvalue: dbSubscription.id,\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst oldPlan = activeOrTrialingSubscription\n\t\t\t\t\t? await getPlanByName(options, activeOrTrialingSubscription.plan)\n\t\t\t\t\t: undefined;\n\n\t\t\t\t// Build a price replacement map:\n\t\t\t\t// oldPriceId -> { newPrice, quantity? }\n\t\t\t\t// This covers base plan, seat, and line item (usage) price changes.\n\t\t\t\tconst priceMap = new Map<\n\t\t\t\t\tstring,\n\t\t\t\t\t{ newPrice: string; quantity?: number }\n\t\t\t\t>();\n\n\t\t\t\tif (isAutoManagedSeats && plan.seatPriceId) {\n\t\t\t\t\tif (\n\t\t\t\t\t\toldPlan?.seatPriceId &&\n\t\t\t\t\t\toldPlan.seatPriceId !== plan.seatPriceId\n\t\t\t\t\t) {\n\t\t\t\t\t\tpriceMap.set(oldPlan.seatPriceId, {\n\t\t\t\t\t\t\tnewPrice: plan.seatPriceId,\n\t\t\t\t\t\t\tquantity: memberCount,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Multiset diff of line item prices.\n\t\t\t\t// old -> -1, new -> +1.\n\t\t\t\t// delta < 0 = remove, delta > 0 = add.\n\t\t\t\tconst lineItemDelta = new Map<string, number>();\n\t\t\t\tfor (const li of oldPlan?.lineItems ?? []) {\n\t\t\t\t\tif (typeof li.price === \"string\")\n\t\t\t\t\t\tlineItemDelta.set(li.price, (lineItemDelta.get(li.price) ?? 0) - 1);\n\t\t\t\t}\n\t\t\t\tfor (const li of plan.lineItems ?? []) {\n\t\t\t\t\tif (typeof li.price === \"string\")\n\t\t\t\t\t\tlineItemDelta.set(li.price, (lineItemDelta.get(li.price) ?? 0) + 1);\n\t\t\t\t}\n\t\t\t\tfor (const [price, delta] of lineItemDelta) {\n\t\t\t\t\tif (delta === 0) lineItemDelta.delete(price);\n\t\t\t\t}\n\n\t\t\t\tlet upgradeUrl: string;\n\t\t\t\tif (ctx.body.scheduleAtPeriodEnd) {\n\t\t\t\t\t// Deferred change:\n\t\t\t\t\t// schedule at billing period end via Subscription Schedules\n\t\t\t\t\tconst schedule = await client.subscriptionSchedules\n\t\t\t\t\t\t.create({\n\t\t\t\t\t\t\tfrom_subscription: activeSubscription.id,\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.catch(async (e) => {\n\t\t\t\t\t\t\tthrow ctx.error(\"BAD_REQUEST\", {\n\t\t\t\t\t\t\t\tmessage: e.message,\n\t\t\t\t\t\t\t\tcode: e.code,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t});\n\n\t\t\t\t\tconst currentPhase = schedule.phases[0];\n\t\t\t\t\tif (!currentPhase) {\n\t\t\t\t\t\tthrow ctx.error(\"BAD_REQUEST\", {\n\t\t\t\t\t\t\tmessage: \"Subscription schedule has no phases\",\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\n\t\t\t\t\tconst removeQuota = new Map<string, number>();\n\t\t\t\t\tfor (const [p, d] of lineItemDelta) {\n\t\t\t\t\t\tif (d < 0) removeQuota.set(p, -d);\n\t\t\t\t\t}\n\n\t\t\t\t\tconst newPhaseItems: Array<{\n\t\t\t\t\t\tprice: string;\n\t\t\t\t\t\tquantity?: number;\n\t\t\t\t\t}> = [];\n\t\t\t\t\tfor (const item of currentPhase.items) {\n\t\t\t\t\t\tconst itemPriceId =\n\t\t\t\t\t\t\ttypeof item.price === \"string\" ? item.price : item.price.id;\n\n\t\t\t\t\t\t// Remove items the new plan no longer needs\n\t\t\t\t\t\tconst quota = removeQuota.get(itemPriceId) ?? 0;\n\t\t\t\t\t\tif (quota > 0) {\n\t\t\t\t\t\t\tremoveQuota.set(itemPriceId, quota - 1);\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// priceMap takes priority\n\t\t\t\t\t\t// which handles seat-only plans\n\t\t\t\t\t\t// where base price === seat price\n\t\t\t\t\t\tconst replacement = priceMap.get(itemPriceId);\n\t\t\t\t\t\tif (replacement) {\n\t\t\t\t\t\t\tnewPhaseItems.push({\n\t\t\t\t\t\t\t\tprice: replacement.newPrice,\n\t\t\t\t\t\t\t\tquantity: replacement.quantity ?? item.quantity,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Replace base plan price\n\t\t\t\t\t\tif (itemPriceId === stripeSubscriptionPriceId) {\n\t\t\t\t\t\t\tnewPhaseItems.push({\n\t\t\t\t\t\t\t\tprice: priceIdToUse,\n\t\t\t\t\t\t\t\tquantity: isAutoManagedSeats ? 1 : ctx.body.seats || 1,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Keep as-is\n\t\t\t\t\t\tnewPhaseItems.push({\n\t\t\t\t\t\t\tprice: itemPriceId,\n\t\t\t\t\t\t\tquantity: item.quantity,\n\t\t\t\t\t\t});\n\t\t\t\t\t\t// Consume positive delta to prevent duplicate addition\n\t\t\t\t\t\tconst d = lineItemDelta.get(itemPriceId);\n\t\t\t\t\t\tif (d !== undefined && d > 0) {\n\t\t\t\t\t\t\tif (d === 1) lineItemDelta.delete(itemPriceId);\n\t\t\t\t\t\t\telse lineItemDelta.set(itemPriceId, d - 1);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t// Add line items the new plan introduces\n\t\t\t\t\tfor (const [price, delta] of lineItemDelta) {\n\t\t\t\t\t\tfor (let i = 0; i < delta; i++) newPhaseItems.push({ price });\n\t\t\t\t\t}\n\n\t\t\t\t\tawait client.subscriptionSchedules\n\t\t\t\t\t\t.update(schedule.id, {\n\t\t\t\t\t\t\tmetadata: { source: \"@better-auth/stripe\" },\n\t\t\t\t\t\t\tend_behavior: \"release\",\n\t\t\t\t\t\t\tphases: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\titems: currentPhase.items.map((item) => ({\n\t\t\t\t\t\t\t\t\t\tprice:\n\t\t\t\t\t\t\t\t\t\t\ttypeof item.price === \"string\"\n\t\t\t\t\t\t\t\t\t\t\t\t? item.price\n\t\t\t\t\t\t\t\t\t\t\t\t: item.price.id,\n\t\t\t\t\t\t\t\t\t\tquantity: item.quantity,\n\t\t\t\t\t\t\t\t\t})),\n\t\t\t\t\t\t\t\t\tstart_date: currentPhase.start_date,\n\t\t\t\t\t\t\t\t\tend_date: currentPhase.end_date,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\titems: newPhaseItems,\n\t\t\t\t\t\t\t\t\tstart_date: currentPhase.end_date,\n\t\t\t\t\t\t\t\t\tproration_behavior: \"none\",\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.catch(async (e) => {\n\t\t\t\t\t\t\tthrow ctx.error(\"BAD_REQUEST\", {\n\t\t\t\t\t\t\t\tmessage: e.message,\n\t\t\t\t\t\t\t\tcode: e.code,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t});\n\n\t\t\t\t\t// Store schedule ID so clients can detect pending plan changes\n\t\t\t\t\tif (dbSubscription) {\n\t\t\t\t\t\tawait ctx.context.adapter.update({\n\t\t\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\t\t\tupdate: {\n\t\t\t\t\t\t\t\tstripeScheduleId: schedule.id,\n\t\t\t\t\t\t\t\tupdatedAt: new Date(),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\twhere: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tfield: \"id\",\n\t\t\t\t\t\t\t\t\tvalue: dbSubscription.id,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\n\t\t\t\t\tupgradeUrl = getUrl(ctx, ctx.body.returnUrl || \"/\");\n\t\t\t\t} else if (priceMap.size > 0 || lineItemDelta.size > 0) {\n\t\t\t\t\t// Immediate change with multi-item updates: use direct API.\n\t\t\t\t\t// Billing Portal supports only 1 item update at a time,\n\t\t\t\t\t// so when multiple prices change between plans we call\n\t\t\t\t\t// subscriptions.update directly.\n\t\t\t\t\tconst removeQuota = new Map<string, number>();\n\t\t\t\t\tfor (const [p, d] of lineItemDelta) {\n\t\t\t\t\t\tif (d < 0) removeQuota.set(p, -d);\n\t\t\t\t\t}\n\n\t\t\t\t\tconst itemUpdates: Array<Record<string, unknown>> = [];\n\t\t\t\t\tfor (const si of activeSubscription.items.data) {\n\t\t\t\t\t\t// Remove items the new plan no longer needs\n\t\t\t\t\t\tconst quota = removeQuota.get(si.price.id) ?? 0;\n\t\t\t\t\t\tif (quota > 0) {\n\t\t\t\t\t\t\tremoveQuota.set(si.price.id, quota - 1);\n\t\t\t\t\t\t\titemUpdates.push({ id: si.id, deleted: true });\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// priceMap takes priority (handles seat-only plans\n\t\t\t\t\t\t// where base price === seat price)\n\t\t\t\t\t\tconst replacement = priceMap.get(si.price.id);\n\t\t\t\t\t\tif (replacement) {\n\t\t\t\t\t\t\titemUpdates.push({\n\t\t\t\t\t\t\t\tid: si.id,\n\t\t\t\t\t\t\t\tprice: replacement.newPrice,\n\t\t\t\t\t\t\t\tquantity: replacement.quantity,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (si.price.id === stripeSubscriptionPriceId) {\n\t\t\t\t\t\t\titemUpdates.push({\n\t\t\t\t\t\t\t\tid: si.id,\n\t\t\t\t\t\t\t\tprice: priceIdToUse,\n\t\t\t\t\t\t\t\tquantity: isAutoManagedSeats ? 1 : ctx.body.seats || 1,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// Consume positive delta to prevent duplicate addition\n\t\t\t\t\t\tconst d = lineItemDelta.get(si.price.id);\n\t\t\t\t\t\tif (d !== undefined && d > 0) {\n\t\t\t\t\t\t\tif (d === 1) lineItemDelta.delete(si.price.id);\n\t\t\t\t\t\t\telse lineItemDelta.set(si.price.id, d - 1);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t// Add line items the new plan introduces\n\t\t\t\t\tfor (const [price, delta] of lineItemDelta) {\n\t\t\t\t\t\tfor (let i = 0; i < delta; i++) itemUpdates.push({ price });\n\t\t\t\t\t}\n\t\t\t\t\tawait client.subscriptions\n\t\t\t\t\t\t.update(activeSubscription.id, {\n\t\t\t\t\t\t\titems: itemUpdates,\n\t\t\t\t\t\t\tproration_behavior: plan.prorationBehavior ?? \"create_prorations\",\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.catch(async (e) => {\n\t\t\t\t\t\t\tthrow ctx.error(\"BAD_REQUEST\", {\n\t\t\t\t\t\t\t\tmessage: e.message,\n\t\t\t\t\t\t\t\tcode: e.code,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t});\n\n\t\t\t\t\tif (dbSubscription) {\n\t\t\t\t\t\tawait ctx.context.adapter.update<Subscription>({\n\t\t\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\t\t\tupdate: {\n\t\t\t\t\t\t\t\tplan: plan.name.toLowerCase(),\n\t\t\t\t\t\t\t\tseats: memberCount,\n\t\t\t\t\t\t\t\tupdatedAt: new Date(),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\twhere: [{ field: \"id\", value: dbSubscription.id }],\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\n\t\t\t\t\tupgradeUrl = getUrl(ctx, ctx.body.returnUrl || \"/\");\n\t\t\t\t} else {\n\t\t\t\t\t// Immediate change via Billing Portal\n\t\t\t\t\t({ url: upgradeUrl } = await client.billingPortal.sessions\n\t\t\t\t\t\t.create({\n\t\t\t\t\t\t\tcustomer: customerId,\n\t\t\t\t\t\t\treturn_url: getUrl(ctx, ctx.body.returnUrl || \"/\"),\n\t\t\t\t\t\t\tflow_data: {\n\t\t\t\t\t\t\t\ttype: \"subscription_update_confirm\",\n\t\t\t\t\t\t\t\tafter_completion: {\n\t\t\t\t\t\t\t\t\ttype: \"redirect\",\n\t\t\t\t\t\t\t\t\tredirect: {\n\t\t\t\t\t\t\t\t\t\treturn_url: getUrl(ctx, ctx.body.returnUrl || \"/\"),\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tsubscription_update_confirm: {\n\t\t\t\t\t\t\t\t\tsubscription: activeSubscription.id,\n\t\t\t\t\t\t\t\t\titems: [\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\tid: planItem.id,\n\t\t\t\t\t\t\t\t\t\t\tprice: priceIdToUse,\n\t\t\t\t\t\t\t\t\t\t\t...(isAutoManagedSeats\n\t\t\t\t\t\t\t\t\t\t\t\t? {}\n\t\t\t\t\t\t\t\t\t\t\t\t: { quantity: ctx.body.seats || 1 }),\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.catch(async (e) => {\n\t\t\t\t\t\t\tthrow ctx.error(\"BAD_REQUEST\", {\n\t\t\t\t\t\t\t\tmessage: e.message,\n\t\t\t\t\t\t\t\tcode: e.code,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}));\n\t\t\t\t}\n\t\t\t\treturn ctx.json({\n\t\t\t\t\turl: upgradeUrl,\n\t\t\t\t\tredirect: !ctx.body.disableRedirect,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tlet subscription: Subscription | undefined =\n\t\t\t\tactiveOrTrialingSubscription || incompleteSubscription;\n\n\t\t\tif (incompleteSubscription && !activeOrTrialingSubscription) {\n\t\t\t\tconst updated = await ctx.context.adapter.update<Subscription>({\n\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\tupdate: {\n\t\t\t\t\t\tplan: plan.name.toLowerCase(),\n\t\t\t\t\t\tseats: isAutoManagedSeats ? memberCount : ctx.body.seats || 1,\n\t\t\t\t\t\tupdatedAt: new Date(),\n\t\t\t\t\t},\n\t\t\t\t\twhere: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfield: \"id\",\n\t\t\t\t\t\t\tvalue: incompleteSubscription.id,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t});\n\t\t\t\tsubscription = (updated as Subscription) || incompleteSubscription;\n\t\t\t}\n\n\t\t\tif (!subscription) {\n\t\t\t\tsubscription = await ctx.context.adapter.create<Subscription>({\n\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\tdata: {\n\t\t\t\t\t\tplan: plan.name.toLowerCase(),\n\t\t\t\t\t\tstripeCustomerId: customerId,\n\t\t\t\t\t\tstatus: \"incomplete\",\n\t\t\t\t\t\treferenceId,\n\t\t\t\t\t\tseats: isAutoManagedSeats ? memberCount : ctx.body.seats || 1,\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (!subscription) {\n\t\t\t\tctx.context.logger.error(\"Subscription ID not found\");\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"NOT_FOUND\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.SUBSCRIPTION_NOT_FOUND,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst params = await subscriptionOptions.getCheckoutSessionParams?.(\n\t\t\t\t{\n\t\t\t\t\tuser,\n\t\t\t\t\tsession,\n\t\t\t\t\tplan,\n\t\t\t\t\tsubscription,\n\t\t\t\t},\n\t\t\t\tctx.request,\n\t\t\t\tctx,\n\t\t\t);\n\n\t\t\tconst allSubscriptions = await ctx.context.adapter.findMany<Subscription>(\n\t\t\t\t{\n\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\twhere: [{ field: \"referenceId\", value: referenceId }],\n\t\t\t\t},\n\t\t\t);\n\t\t\tconst hasEverTrialed = allSubscriptions.some((s) => {\n\t\t\t\t// Check if user has ever had a trial for any plan (not just the same plan)\n\t\t\t\t// This prevents users from getting multiple trials by switching plans\n\t\t\t\tconst hadTrial =\n\t\t\t\t\t!!(s.trialStart || s.trialEnd) || s.status === \"trialing\";\n\t\t\t\treturn hadTrial;\n\t\t\t});\n\n\t\t\tconst freeTrial =\n\t\t\t\t!hasEverTrialed && plan.freeTrial\n\t\t\t\t\t? { trial_period_days: plan.freeTrial.days }\n\t\t\t\t\t: undefined;\n\n\t\t\tconst checkoutSession = await client.checkout.sessions\n\t\t\t\t.create(\n\t\t\t\t\t{\n\t\t\t\t\t\t...(customerId\n\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\tcustomer: customerId,\n\t\t\t\t\t\t\t\t\tcustomer_update:\n\t\t\t\t\t\t\t\t\t\tcustomerType !== \"user\"\n\t\t\t\t\t\t\t\t\t\t\t? ({ address: \"auto\" } as const)\n\t\t\t\t\t\t\t\t\t\t\t: ({ name: \"auto\", address: \"auto\" } as const), // The customer name is automatically set only for users\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t: {\n\t\t\t\t\t\t\t\t\tcustomer_email: user.email,\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\tlocale: ctx.body.locale,\n\t\t\t\t\t\tsuccess_url: getUrl(\n\t\t\t\t\t\t\tctx,\n\t\t\t\t\t\t\t`${\n\t\t\t\t\t\t\t\tctx.context.baseURL\n\t\t\t\t\t\t\t}/subscription/success?callbackURL=${encodeURIComponent(\n\t\t\t\t\t\t\t\tctx.body.successUrl,\n\t\t\t\t\t\t\t\t// {CHECKOUT_SESSION_ID} is a string literal; do not change it!\n\t\t\t\t\t\t\t\t// the actual Session ID is returned in the query parameter when your customer\n\t\t\t\t\t\t\t\t// is redirected to the success page.\n\t\t\t\t\t\t\t)}&checkoutSessionId={CHECKOUT_SESSION_ID}`,\n\t\t\t\t\t\t),\n\t\t\t\t\t\tcancel_url: getUrl(ctx, ctx.body.cancelUrl),\n\t\t\t\t\t\tline_items: [\n\t\t\t\t\t\t\t// Base price\n\t\t\t\t\t\t\t...(!isSeatOnlyPlan\n\t\t\t\t\t\t\t\t? [\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\tprice: priceIdToUse,\n\t\t\t\t\t\t\t\t\t\t\tquantity: isAutoManagedSeats ? 1 : ctx.body.seats || 1,\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t: []),\n\t\t\t\t\t\t\t// Per-seat\n\t\t\t\t\t\t\t...(isAutoManagedSeats\n\t\t\t\t\t\t\t\t? [{ price: plan.seatPriceId, quantity: memberCount }]\n\t\t\t\t\t\t\t\t: []),\n\t\t\t\t\t\t\t// Additional line items (metered prices, add-ons, etc.)\n\t\t\t\t\t\t\t...(plan.lineItems ?? []),\n\t\t\t\t\t\t],\n\t\t\t\t\t\tsubscription_data: {\n\t\t\t\t\t\t\t...freeTrial,\n\t\t\t\t\t\t\tmetadata: subscriptionMetadata.set(\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tuserId: user.id,\n\t\t\t\t\t\t\t\t\tsubscriptionId: subscription.id,\n\t\t\t\t\t\t\t\t\treferenceId,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tctx.body.metadata,\n\t\t\t\t\t\t\t\tparams?.params?.subscription_data?.metadata,\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t},\n\t\t\t\t\t\tmode: \"subscription\",\n\t\t\t\t\t\tclient_reference_id: referenceId,\n\t\t\t\t\t\t...params?.params,\n\t\t\t\t\t\t// metadata should come after spread to protect internal fields\n\t\t\t\t\t\tmetadata: subscriptionMetadata.set(\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tuserId: user.id,\n\t\t\t\t\t\t\t\tsubscriptionId: subscription.id,\n\t\t\t\t\t\t\t\treferenceId,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tctx.body.metadata,\n\t\t\t\t\t\t\tparams?.params?.metadata,\n\t\t\t\t\t\t),\n\t\t\t\t\t},\n\t\t\t\t\tparams?.options,\n\t\t\t\t)\n\t\t\t\t.catch(async (e) => {\n\t\t\t\t\tthrow ctx.error(\"BAD_REQUEST\", {\n\t\t\t\t\t\tmessage: e.message,\n\t\t\t\t\t\tcode: e.code,\n\t\t\t\t\t});\n\t\t\t\t});\n\t\t\treturn ctx.json({\n\t\t\t\t...checkoutSession,\n\t\t\t\tredirect: !ctx.body.disableRedirect,\n\t\t\t});\n\t\t},\n\t);\n};\n\nconst cancelSubscriptionBodySchema = z.object({\n\treferenceId: z\n\t\t.string()\n\t\t.meta({\n\t\t\tdescription: \"Reference id of the subscription to cancel. Eg: '123'\",\n\t\t})\n\t\t.optional(),\n\tsubscriptionId: z\n\t\t.string()\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t\"The Stripe subscription ID to cancel. Eg: 'sub_1ABC2DEF3GHI4JKL'\",\n\t\t})\n\t\t.optional(),\n\t/**\n\t * Customer type for the subscription.\n\t * - `user`: User owns the subscription (default)\n\t * - `organization`: Organization owns the subscription\n\t */\n\tcustomerType: z\n\t\t.enum([\"user\", \"organization\"])\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t'Customer type for the subscription. Eg: \"user\" or \"organization\"',\n\t\t})\n\t\t.optional(),\n\treturnUrl: z.string().meta({\n\t\tdescription:\n\t\t\t'URL to take customers to when they click on the billing portal\\'s link to return to your website. Eg: \"/account\"',\n\t}),\n\t/**\n\t * Disable Redirect\n\t */\n\tdisableRedirect: z\n\t\t.boolean()\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t\"Disable redirect after successful subscription cancellation. Eg: true\",\n\t\t})\n\t\t.default(false),\n});\n\n/**\n * ### Endpoint\n *\n * POST `/subscription/cancel`\n *\n * ### API Methods\n *\n * **server:**\n * `auth.api.cancelSubscription`\n *\n * **client:**\n * `authClient.subscription.cancel`\n *\n * @see [Read our docs to learn more.](https://better-auth.com/docs/plugins/stripe#api-method-subscription-cancel)\n */\nexport const cancelSubscription = (options: StripeOptions) => {\n\tconst client = options.stripeClient;\n\tconst subscriptionOptions = options.subscription as SubscriptionOptions;\n\treturn createAuthEndpoint(\n\t\t\"/subscription/cancel\",\n\t\t{\n\t\t\tmethod: \"POST\",\n\t\t\tbody: cancelSubscriptionBodySchema,\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\toperationId: \"cancelSubscription\",\n\t\t\t\t},\n\t\t\t},\n\t\t\tuse: [\n\t\t\t\tstripeSessionMiddleware,\n\t\t\t\treferenceMiddleware(subscriptionOptions, \"cancel-subscription\"),\n\t\t\t\toriginCheck((ctx) => ctx.body.returnUrl),\n\t\t\t],\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tconst customerType = ctx.body.customerType || \"user\";\n\t\t\tconst referenceId =\n\t\t\t\tctx.body.referenceId ||\n\t\t\t\tgetReferenceId(ctx.context.session, customerType, options);\n\n\t\t\tlet subscription = ctx.body.subscriptionId\n\t\t\t\t? await ctx.context.adapter.findOne<Subscription>({\n\t\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\t\twhere: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tfield: \"stripeSubscriptionId\",\n\t\t\t\t\t\t\t\tvalue: ctx.body.subscriptionId,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t})\n\t\t\t\t: await ctx.context.adapter\n\t\t\t\t\t\t.findMany<Subscription>({\n\t\t\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\t\t\twhere: [{ field: \"referenceId\", value: referenceId }],\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.then((subs) => subs.find((sub) => isActiveOrTrialing(sub)));\n\t\t\tif (\n\t\t\t\tctx.body.subscriptionId &&\n\t\t\t\tsubscription &&\n\t\t\t\tsubscription.referenceId !== referenceId\n\t\t\t) {\n\t\t\t\tsubscription = undefined;\n\t\t\t}\n\n\t\t\tif (!subscription || !subscription.stripeCustomerId) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.SUBSCRIPTION_NOT_FOUND,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst activeSubscriptions = await client.subscriptions\n\t\t\t\t.list({\n\t\t\t\t\tcustomer: subscription.stripeCustomerId,\n\t\t\t\t})\n\t\t\t\t.then((res) => res.data.filter((sub) => isActiveOrTrialing(sub)));\n\t\t\tif (!activeSubscriptions.length) {\n\t\t\t\t/**\n\t\t\t\t * If the subscription is not found, we need to delete the subscription\n\t\t\t\t * from the database. This is a rare case and should not happen.\n\t\t\t\t */\n\t\t\t\tawait ctx.context.adapter.deleteMany({\n\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\twhere: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfield: \"referenceId\",\n\t\t\t\t\t\t\tvalue: referenceId,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t});\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.SUBSCRIPTION_NOT_FOUND,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst activeSubscription = activeSubscriptions.find(\n\t\t\t\t(sub) => sub.id === subscription.stripeSubscriptionId,\n\t\t\t);\n\t\t\tif (!activeSubscription) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.SUBSCRIPTION_NOT_FOUND,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst { url } = await client.billingPortal.sessions\n\t\t\t\t.create({\n\t\t\t\t\tcustomer: subscription.stripeCustomerId,\n\t\t\t\t\treturn_url: getUrl(ctx, ctx.body?.returnUrl || \"/\"),\n\t\t\t\t\tflow_data: {\n\t\t\t\t\t\ttype: \"subscription_cancel\",\n\t\t\t\t\t\tsubscription_cancel: {\n\t\t\t\t\t\t\tsubscription: activeSubscription.id,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t\t\t.catch(async (e) => {\n\t\t\t\t\tif (e.message?.includes(\"already set to be canceled\")) {\n\t\t\t\t\t\t/**\n\t\t\t\t\t\t * in-case we missed the event from stripe, we sync the actual state\n\t\t\t\t\t\t * this is a rare case and should not happen\n\t\t\t\t\t\t */\n\t\t\t\t\t\tif (!isPendingCancel(subscription)) {\n\t\t\t\t\t\t\tconst stripeSub = await client.subscriptions.retrieve(\n\t\t\t\t\t\t\t\tactiveSubscription.id,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tawait ctx.context.adapter.update({\n\t\t\t\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\t\t\t\tupdate: {\n\t\t\t\t\t\t\t\t\tcancelAtPeriodEnd: stripeSub.cancel_at_period_end,\n\t\t\t\t\t\t\t\t\tcancelAt: stripeSub.cancel_at\n\t\t\t\t\t\t\t\t\t\t? new Date(stripeSub.cancel_at * 1000)\n\t\t\t\t\t\t\t\t\t\t: null,\n\t\t\t\t\t\t\t\t\tcanceledAt: stripeSub.canceled_at\n\t\t\t\t\t\t\t\t\t\t? new Date(stripeSub.canceled_at * 1000)\n\t\t\t\t\t\t\t\t\t\t: null,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\twhere: [\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tfield: \"id\",\n\t\t\t\t\t\t\t\t\t\tvalue: subscription.id,\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tthrow ctx.error(\"BAD_REQUEST\", {\n\t\t\t\t\t\tmessage: e.message,\n\t\t\t\t\t\tcode: e.code,\n\t\t\t\t\t});\n\t\t\t\t});\n\t\t\treturn ctx.json({\n\t\t\t\turl,\n\t\t\t\tredirect: !ctx.body.disableRedirect,\n\t\t\t});\n\t\t},\n\t);\n};\n\nconst restoreSubscriptionBodySchema = z.object({\n\treferenceId: z\n\t\t.string()\n\t\t.meta({\n\t\t\tdescription: \"Reference id of the subscription to restore. Eg: '123'\",\n\t\t})\n\t\t.optional(),\n\tsubscriptionId: z\n\t\t.string()\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t\"The Stripe subscription ID to restore. Eg: 'sub_1ABC2DEF3GHI4JKL'\",\n\t\t})\n\t\t.optional(),\n\t/**\n\t * Customer type for the subscription.\n\t * - `user`: User owns the subscription (default)\n\t * - `organization`: Organization owns the subscription\n\t */\n\tcustomerType: z\n\t\t.enum([\"user\", \"organization\"])\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t'Customer type for the subscription. Eg: \"user\" or \"organization\"',\n\t\t})\n\t\t.optional(),\n});\n\nexport const restoreSubscription = (options: StripeOptions) => {\n\tconst client = options.stripeClient;\n\tconst subscriptionOptions = options.subscription as SubscriptionOptions;\n\treturn createAuthEndpoint(\n\t\t\"/subscription/restore\",\n\t\t{\n\t\t\tmethod: \"POST\",\n\t\t\tbody: restoreSubscriptionBodySchema,\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\toperationId: \"restoreSubscription\",\n\t\t\t\t},\n\t\t\t},\n\t\t\tuse: [\n\t\t\t\tstripeSessionMiddleware,\n\t\t\t\treferenceMiddleware(subscriptionOptions, \"restore-subscription\"),\n\t\t\t],\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tconst customerType = ctx.body.customerType || \"user\";\n\t\t\tconst referenceId =\n\t\t\t\tctx.body.referenceId ||\n\t\t\t\tgetReferenceId(ctx.context.session, customerType, options);\n\n\t\t\tlet subscription = ctx.body.subscriptionId\n\t\t\t\t? await ctx.context.adapter.findOne<Subscription>({\n\t\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\t\twhere: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tfield: \"stripeSubscriptionId\",\n\t\t\t\t\t\t\t\tvalue: ctx.body.subscriptionId,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t})\n\t\t\t\t: await ctx.context.adapter\n\t\t\t\t\t\t.findMany<Subscription>({\n\t\t\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\t\t\twhere: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tfield: \"referenceId\",\n\t\t\t\t\t\t\t\t\tvalue: referenceId,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.then((subs) => subs.find((sub) => isActiveOrTrialing(sub)));\n\t\t\tif (\n\t\t\t\tctx.body.subscriptionId &&\n\t\t\t\tsubscription &&\n\t\t\t\tsubscription.referenceId !== referenceId\n\t\t\t) {\n\t\t\t\tsubscription = undefined;\n\t\t\t}\n\t\t\tif (!subscription || !subscription.stripeCustomerId) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.SUBSCRIPTION_NOT_FOUND,\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (!isActiveOrTrialing(subscription)) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.SUBSCRIPTION_NOT_ACTIVE,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst hasPendingCancel = isPendingCancel(subscription);\n\t\t\tconst { stripeScheduleId } = subscription;\n\n\t\t\tif (!hasPendingCancel && !stripeScheduleId) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.SUBSCRIPTION_NOT_PENDING_CHANGE,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Pending cancel and pending schedule are mutually exclusive in Stripe.\n\t\t\tif (stripeScheduleId) {\n\t\t\t\tif (!subscription.stripeSubscriptionId) {\n\t\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\t\tSTRIPE_ERROR_CODES.SUBSCRIPTION_NOT_FOUND,\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tconst schedule = await client.subscriptionSchedules\n\t\t\t\t\t.retrieve(stripeScheduleId)\n\t\t\t\t\t.catch((e) => {\n\t\t\t\t\t\tthrow ctx.error(\"BAD_REQUEST\", {\n\t\t\t\t\t\t\tmessage: e.message,\n\t\t\t\t\t\t\tcode: e.code,\n\t\t\t\t\t\t});\n\t\t\t\t\t});\n\n\t\t\t\tif (schedule.status === \"active\") {\n\t\t\t\t\tawait client.subscriptionSchedules\n\t\t\t\t\t\t.release(stripeScheduleId)\n\t\t\t\t\t\t.catch((e) => {\n\t\t\t\t\t\t\tthrow ctx.error(\"BAD_REQUEST\", {\n\t\t\t\t\t\t\t\tmessage: e.message,\n\t\t\t\t\t\t\t\tcode: e.code,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\tawait ctx.context.adapter.update({\n\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\tupdate: {\n\t\t\t\t\t\tstripeScheduleId: null,\n\t\t\t\t\t\tupdatedAt: new Date(),\n\t\t\t\t\t},\n\t\t\t\t\twhere: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfield: \"id\",\n\t\t\t\t\t\t\tvalue: subscription.id,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t});\n\t\t\t\tconst releasedSub = await client.subscriptions.retrieve(\n\t\t\t\t\tsubscription.stripeSubscriptionId,\n\t\t\t\t);\n\t\t\t\treturn ctx.json(releasedSub);\n\t\t\t}\n\n\t\t\t// Handle pending cancellation\n\t\t\tconst activeSubscription = await client.subscriptions\n\t\t\t\t.list({\n\t\t\t\t\tcustomer: subscription.stripeCustomerId,\n\t\t\t\t})\n\t\t\t\t.then((res) => res.data.filter((sub) => isActiveOrTrialing(sub))[0]);\n\t\t\tif (!activeSubscription) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.SUBSCRIPTION_NOT_FOUND,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Clear scheduled cancellation based on Stripe subscription state\n\t\t\t// Note: Stripe doesn't accept both `cancel_at` and `cancel_at_period_end` simultaneously\n\t\t\tconst updateParams: Stripe.SubscriptionUpdateParams = {};\n\t\t\tif (activeSubscription.cancel_at) {\n\t\t\t\tupdateParams.cancel_at = \"\";\n\t\t\t} else if (activeSubscription.cancel_at_period_end) {\n\t\t\t\tupdateParams.cancel_at_period_end = false;\n\t\t\t}\n\n\t\t\tconst newSub = await client.subscriptions\n\t\t\t\t.update(activeSubscription.id, updateParams)\n\t\t\t\t.catch((e) => {\n\t\t\t\t\tthrow ctx.error(\"BAD_REQUEST\", {\n\t\t\t\t\t\tmessage: e.message,\n\t\t\t\t\t\tcode: e.code,\n\t\t\t\t\t});\n\t\t\t\t});\n\n\t\t\tawait ctx.context.adapter.update({\n\t\t\t\tmodel: \"subscription\",\n\t\t\t\tupdate: {\n\t\t\t\t\tcancelAtPeriodEnd: false,\n\t\t\t\t\tcancelAt: null,\n\t\t\t\t\tcanceledAt: null,\n\t\t\t\t\tupdatedAt: new Date(),\n\t\t\t\t},\n\t\t\t\twhere: [\n\t\t\t\t\t{\n\t\t\t\t\t\tfield: \"id\",\n\t\t\t\t\t\tvalue: subscription.id,\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t});\n\n\t\t\treturn ctx.json(newSub);\n\t\t},\n\t);\n};\n\nconst listActiveSubscriptionsQuerySchema = z.optional(\n\tz.object({\n\t\treferenceId: z\n\t\t\t.string()\n\t\t\t.meta({\n\t\t\t\tdescription: \"Reference id of the subscription to list. Eg: '123'\",\n\t\t\t})\n\t\t\t.optional(),\n\t\t/**\n\t\t * Customer type for the subscription.\n\t\t * - `user`: User owns the subscription (default)\n\t\t * - `organization`: Organization owns the subscription\n\t\t */\n\t\tcustomerType: z\n\t\t\t.enum([\"user\", \"organization\"])\n\t\t\t.meta({\n\t\t\t\tdescription:\n\t\t\t\t\t'Customer type for the subscription. Eg: \"user\" or \"organization\"',\n\t\t\t})\n\t\t\t.optional(),\n\t}),\n);\n/**\n * ### Endpoint\n *\n * GET `/subscription/list`\n *\n * ### API Methods\n *\n * **server:**\n * `auth.api.listActiveSubscriptions`\n *\n * **client:**\n * `authClient.subscription.list`\n *\n * @see [Read our docs to learn more.](https://better-auth.com/docs/plugins/stripe#api-method-subscription-list)\n */\nexport const listActiveSubscriptions = (options: StripeOptions) => {\n\tconst subscriptionOptions = options.subscription as SubscriptionOptions;\n\treturn createAuthEndpoint(\n\t\t\"/subscription/list\",\n\t\t{\n\t\t\tmethod: \"GET\",\n\t\t\tquery: listActiveSubscriptionsQuerySchema,\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\toperationId: \"listActiveSubscriptions\",\n\t\t\t\t},\n\t\t\t},\n\t\t\tuse: [\n\t\t\t\tstripeSessionMiddleware,\n\t\t\t\treferenceMiddleware(subscriptionOptions, \"list-subscription\"),\n\t\t\t],\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tconst customerType = ctx.query?.customerType || \"user\";\n\t\t\tconst referenceId =\n\t\t\t\tctx.query?.referenceId ||\n\t\t\t\tgetReferenceId(ctx.context.session, customerType, options);\n\n\t\t\tconst subscriptions = await ctx.context.adapter.findMany<Subscription>({\n\t\t\t\tmodel: \"subscription\",\n\t\t\t\twhere: [\n\t\t\t\t\t{\n\t\t\t\t\t\tfield: \"referenceId\",\n\t\t\t\t\t\tvalue: referenceId,\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t});\n\t\t\tif (!subscriptions.length) {\n\t\t\t\treturn [];\n\t\t\t}\n\t\t\tconst plans = await getPlans(options.subscription);\n\t\t\tif (!plans) {\n\t\t\t\treturn [];\n\t\t\t}\n\t\t\tconst subs = subscriptions\n\t\t\t\t.map((sub) => {\n\t\t\t\t\tconst plan = plans.find(\n\t\t\t\t\t\t(p) => p.name.toLowerCase() === sub.plan.toLowerCase(),\n\t\t\t\t\t);\n\t\t\t\t\treturn {\n\t\t\t\t\t\t...sub,\n\t\t\t\t\t\tlimits: plan?.limits,\n\t\t\t\t\t\tpriceId: plan?.priceId,\n\t\t\t\t\t};\n\t\t\t\t})\n\t\t\t\t.filter((sub) => isActiveOrTrialing(sub));\n\t\t\treturn ctx.json(subs);\n\t\t},\n\t);\n};\n\nconst subscriptionSuccessQuerySchema = z.record(z.string(), z.any()).optional();\n\nexport const subscriptionSuccess = (options: StripeOptions) => {\n\tconst client = options.stripeClient;\n\treturn createAuthEndpoint(\n\t\t\"/subscription/success\",\n\t\t{\n\t\t\tmethod: \"GET\",\n\t\t\tquery: subscriptionSuccessQuerySchema,\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\toperationId: \"handleSubscriptionSuccess\",\n\t\t\t\t},\n\t\t\t},\n\t\t\tuse: [originCheck((ctx) => ctx.query.callbackURL)],\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tlet callbackURL = ctx.query?.callbackURL || \"/\";\n\n\t\t\tconst session = await getSessionFromCtx<User & WithStripeCustomerId>(ctx);\n\t\t\tif (!session) {\n\t\t\t\tthrow ctx.redirect(getUrl(ctx, callbackURL));\n\t\t\t}\n\n\t\t\t// checkoutSessionId is substituted by Stripe from the {CHECKOUT_SESSION_ID}\n\t\t\t// template variable in success_url when redirecting after checkout.\n\t\t\tif (!ctx.query?.checkoutSessionId) {\n\t\t\t\tthrow ctx.redirect(getUrl(ctx, callbackURL));\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Replace the Stripe {CHECKOUT_SESSION_ID} template variable in callbackURL.\n\t\t\t * @see https://docs.stripe.com/payments/checkout/custom-success-page?payment-ui=stripe-hosted#modify-the-success-url\n\t\t\t */\n\t\t\tcallbackURL = callbackURL.replaceAll(\n\t\t\t\t\"{CHECKOUT_SESSION_ID}\",\n\t\t\t\tctx.query.checkoutSessionId,\n\t\t\t);\n\n\t\t\t// Resolve subscriptionId from Stripe checkout session metadata.\n\t\t\t// The metadata is set server-side when creating the checkout session,\n\t\t\t// so it cannot be tampered with — no ownership check needed.\n\t\t\tconst checkoutSession = await client.checkout.sessions\n\t\t\t\t.retrieve(ctx.query.checkoutSessionId)\n\t\t\t\t.catch((error) => {\n\t\t\t\t\tctx.context.logger.error(\n\t\t\t\t\t\t\"Error retrieving checkout session from Stripe\",\n\t\t\t\t\t\terror,\n\t\t\t\t\t);\n\t\t\t\t\treturn null;\n\t\t\t\t});\n\t\t\tif (!checkoutSession) {\n\t\t\t\tthrow ctx.redirect(getUrl(ctx, callbackURL));\n\t\t\t}\n\n\t\t\tconst { subscriptionId } = subscriptionMetadata.get(\n\t\t\t\tcheckoutSession.metadata,\n\t\t\t);\n\t\t\tif (!subscriptionId) {\n\t\t\t\tctx.context.logger.warn(\n\t\t\t\t\t`No subscriptionId in checkout session metadata: ${checkoutSession.id}`,\n\t\t\t\t);\n\t\t\t\tthrow ctx.redirect(getUrl(ctx, callbackURL));\n\t\t\t}\n\n\t\t\tconst subscription = await ctx.context.adapter.findOne<Subscription>({\n\t\t\t\tmodel: \"subscription\",\n\t\t\t\twhere: [\n\t\t\t\t\t{\n\t\t\t\t\t\tfield: \"id\",\n\t\t\t\t\t\tvalue: subscriptionId,\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t});\n\t\t\tif (!subscription) {\n\t\t\t\tctx.context.logger.warn(\n\t\t\t\t\t`Subscription record not found for subscriptionId: ${subscriptionId}`,\n\t\t\t\t);\n\t\t\t\tthrow ctx.redirect(getUrl(ctx, callbackURL));\n\t\t\t}\n\n\t\t\t// Already active or trialing, no need to update\n\t\t\tif (isActiveOrTrialing(subscription)) {\n\t\t\t\tthrow ctx.redirect(getUrl(ctx, callbackURL));\n\t\t\t}\n\n\t\t\tconst customerId =\n\t\t\t\tsubscription.stripeCustomerId || session.user.stripeCustomerId;\n\t\t\tif (!customerId) {\n\t\t\t\tthrow ctx.redirect(getUrl(ctx, callbackURL));\n\t\t\t}\n\n\t\t\tconst stripeSubscription = await client.subscriptions\n\t\t\t\t.list({ customer: customerId, status: \"active\" })\n\t\t\t\t.then((res) => res.data[0])\n\t\t\t\t.catch((error) => {\n\t\t\t\t\tctx.context.logger.error(\n\t\t\t\t\t\t\"Error fetching subscription from Stripe\",\n\t\t\t\t\t\terror,\n\t\t\t\t\t);\n\t\t\t\t\tthrow ctx.redirect(getUrl(ctx, callbackURL));\n\t\t\t\t});\n\t\t\tif (!stripeSubscription) {\n\t\t\t\tthrow ctx.redirect(getUrl(ctx, callbackURL));\n\t\t\t}\n\n\t\t\tconst resolved = await resolvePlanItem(\n\t\t\t\toptions,\n\t\t\t\tstripeSubscription.items.data,\n\t\t\t);\n\t\t\tif (!resolved) {\n\t\t\t\tctx.context.logger.warn(\n\t\t\t\t\t`No subscription items found for Stripe subscription ${stripeSubscription.id}`,\n\t\t\t\t);\n\t\t\t\tthrow ctx.redirect(getUrl(ctx, callbackURL));\n\t\t\t}\n\n\t\t\tconst { item: subscriptionItem, plan } = resolved;\n\t\t\tif (!plan) {\n\t\t\t\tctx.context.logger.warn(\n\t\t\t\t\t`Plan not found for price ${subscriptionItem.price.id}`,\n\t\t\t\t);\n\t\t\t\tthrow ctx.redirect(getUrl(ctx, callbackURL));\n\t\t\t}\n\n\t\t\tconst seats =\n\t\t\t\tresolveQuantity(\n\t\t\t\t\tstripeSubscription.items.data,\n\t\t\t\t\tsubscriptionItem,\n\t\t\t\t\tplan.seatPriceId,\n\t\t\t\t) || 1;\n\n\t\t\tawait ctx.context.adapter.update({\n\t\t\t\tmodel: \"subscription\",\n\t\t\t\tupdate: {\n\t\t\t\t\t...(stripeSubscription.trial_start && stripeSubscription.trial_end\n\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\ttrialStart: new Date(stripeSubscription.trial_start * 1000),\n\t\t\t\t\t\t\t\ttrialEnd: new Date(stripeSubscription.trial_end * 1000),\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t: {}),\n\t\t\t\t\tstatus: stripeSubscription.status,\n\t\t\t\t\tseats,\n\t\t\t\t\tplan: plan.name.toLowerCase(),\n\t\t\t\t\tbillingInterval: subscriptionItem.price.recurring?.interval,\n\t\t\t\t\tperiodEnd: new Date(subscriptionItem.current_period_end * 1000),\n\t\t\t\t\tperiodStart: new Date(subscriptionItem.current_period_start * 1000),\n\t\t\t\t\tstripeSubscriptionId: stripeSubscription.id,\n\t\t\t\t\tcancelAtPeriodEnd: stripeSubscription.cancel_at_period_end,\n\t\t\t\t\tcancelAt: stripeSubscription.cancel_at\n\t\t\t\t\t\t? new Date(stripeSubscription.cancel_at * 1000)\n\t\t\t\t\t\t: null,\n\t\t\t\t\tcanceledAt: stripeSubscription.canceled_at\n\t\t\t\t\t\t? new Date(stripeSubscription.canceled_at * 1000)\n\t\t\t\t\t\t: null,\n\t\t\t\t},\n\t\t\t\twhere: [\n\t\t\t\t\t{\n\t\t\t\t\t\tfield: \"id\",\n\t\t\t\t\t\tvalue: subscription.id,\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t});\n\n\t\t\tthrow ctx.redirect(getUrl(ctx, callbackURL));\n\t\t},\n\t);\n};\n\nconst createBillingPortalBodySchema = z.object({\n\t/**\n\t * The IETF language tag of the locale Customer Portal is displayed in.\n\t * If not provided or set to `auto`, the browser's locale is used.\n\t */\n\tlocale: z\n\t\t.custom<StripeType.Checkout.Session.Locale>((localization) => {\n\t\t\treturn typeof localization === \"string\";\n\t\t})\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t\"The IETF language tag of the locale Customer Portal is displayed in. Eg: 'en', 'ko'. If not provided or set to `auto`, the browser's locale is used.\",\n\t\t})\n\t\t.optional(),\n\treferenceId: z.string().optional(),\n\t/**\n\t * Customer type for the subscription.\n\t * - `user`: User owns the subscription (default)\n\t * - `organization`: Organization owns the subscription\n\t */\n\tcustomerType: z\n\t\t.enum([\"user\", \"organization\"])\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t'Customer type for the subscription. Eg: \"user\" or \"organization\"',\n\t\t})\n\t\t.optional(),\n\treturnUrl: z.string().default(\"/\"),\n\t/**\n\t * Disable Redirect\n\t */\n\tdisableRedirect: z\n\t\t.boolean()\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t\"Disable redirect after creating billing portal session. Eg: true\",\n\t\t})\n\t\t.default(false),\n});\n\nexport const createBillingPortal = (options: StripeOptions) => {\n\tconst client = options.stripeClient;\n\tconst subscriptionOptions = options.subscription as SubscriptionOptions;\n\treturn createAuthEndpoint(\n\t\t\"/subscription/billing-portal\",\n\t\t{\n\t\t\tmethod: \"POST\",\n\t\t\tbody: createBillingPortalBodySchema,\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\toperationId: \"createBillingPortal\",\n\t\t\t\t},\n\t\t\t},\n\t\t\tuse: [\n\t\t\t\tstripeSessionMiddleware,\n\t\t\t\treferenceMiddleware(subscriptionOptions, \"billing-portal\"),\n\t\t\t\toriginCheck((ctx) => ctx.body.returnUrl),\n\t\t\t],\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tconst { user } = ctx.context.session;\n\t\t\tconst customerType = ctx.body.customerType || \"user\";\n\t\t\tconst referenceId =\n\t\t\t\tctx.body.referenceId ||\n\t\t\t\tgetReferenceId(ctx.context.session, customerType, options);\n\n\t\t\tlet customerId: string | undefined;\n\n\t\t\tif (customerType === \"organization\") {\n\t\t\t\t// Organization billing portal - get customer ID from organization\n\t\t\t\tconst org = await ctx.context.adapter.findOne<\n\t\t\t\t\tOrganization & WithStripeCustomerId\n\t\t\t\t>({\n\t\t\t\t\tmodel: \"organization\",\n\t\t\t\t\twhere: [{ field: \"id\", value: referenceId }],\n\t\t\t\t});\n\t\t\t\tcustomerId = org?.stripeCustomerId;\n\n\t\t\t\tif (!customerId) {\n\t\t\t\t\t// Fallback to subscription's stripeCustomerId\n\t\t\t\t\tconst subscription = await ctx.context.adapter\n\t\t\t\t\t\t.findMany<Subscription>({\n\t\t\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\t\t\twhere: [{ field: \"referenceId\", value: referenceId }],\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.then((subs) => subs.find((sub) => isActiveOrTrialing(sub)));\n\t\t\t\t\tcustomerId = subscription?.stripeCustomerId;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// User billing portal\n\t\t\t\tcustomerId = user.stripeCustomerId;\n\t\t\t\tif (!customerId) {\n\t\t\t\t\tconst subscription = await ctx.context.adapter\n\t\t\t\t\t\t.findMany<Subscription>({\n\t\t\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\t\t\twhere: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tfield: \"referenceId\",\n\t\t\t\t\t\t\t\t\tvalue: referenceId,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.then((subs) => subs.find((sub) => isActiveOrTrialing(sub)));\n\n\t\t\t\t\tcustomerId = subscription?.stripeCustomerId;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!customerId) {\n\t\t\t\tthrow APIError.from(\"NOT_FOUND\", STRIPE_ERROR_CODES.CUSTOMER_NOT_FOUND);\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst { url } = await client.billingPortal.sessions.create({\n\t\t\t\t\tlocale: ctx.body.locale,\n\t\t\t\t\tcustomer: customerId,\n\t\t\t\t\treturn_url: getUrl(ctx, ctx.body.returnUrl),\n\t\t\t\t});\n\n\t\t\t\treturn ctx.json({\n\t\t\t\t\turl,\n\t\t\t\t\tredirect: !ctx.body.disableRedirect,\n\t\t\t\t});\n\t\t\t} catch (error: any) {\n\t\t\t\tctx.context.logger.error(\n\t\t\t\t\t\"Error creating billing portal session\",\n\t\t\t\t\terror,\n\t\t\t\t);\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"INTERNAL_SERVER_ERROR\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.UNABLE_TO_CREATE_BILLING_PORTAL,\n\t\t\t\t);\n\t\t\t}\n\t\t},\n\t);\n};\n\nexport const stripeWebhook = (options: StripeOptions) => {\n\tconst client = options.stripeClient;\n\treturn createAuthEndpoint(\n\t\t\"/stripe/webhook\",\n\t\t{\n\t\t\tmethod: \"POST\",\n\t\t\tmetadata: {\n\t\t\t\t...HIDE_METADATA,\n\t\t\t\topenapi: {\n\t\t\t\t\toperationId: \"handleStripeWebhook\",\n\t\t\t\t},\n\t\t\t},\n\t\t\tcloneRequest: true,\n\t\t\tdisableBody: true, // Don't parse the body\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tif (!ctx.request?.body) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.INVALID_REQUEST_BODY,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst sig = ctx.request.headers.get(\"stripe-signature\");\n\t\t\tif (!sig) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.STRIPE_SIGNATURE_NOT_FOUND,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst webhookSecret = options.stripeWebhookSecret;\n\t\t\tif (!webhookSecret) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"INTERNAL_SERVER_ERROR\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.STRIPE_WEBHOOK_SECRET_NOT_FOUND,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst payload = await ctx.request.text();\n\n\t\t\tlet event: Stripe.Event;\n\t\t\ttry {\n\t\t\t\t// Support both Stripe v18 (constructEvent) and v19+ (constructEventAsync)\n\t\t\t\tif (typeof client.webhooks.constructEventAsync === \"function\") {\n\t\t\t\t\t// Stripe v19+ - use async method\n\t\t\t\t\tevent = await client.webhooks.constructEventAsync(\n\t\t\t\t\t\tpayload,\n\t\t\t\t\t\tsig,\n\t\t\t\t\t\twebhookSecret,\n\t\t\t\t\t);\n\t\t\t\t} else {\n\t\t\t\t\t// Stripe v18 - use sync method\n\t\t\t\t\tevent = client.webhooks.constructEvent(payload, sig, webhookSecret);\n\t\t\t\t}\n\t\t\t} catch (err: any) {\n\t\t\t\tctx.context.logger.error(`${err.message}`);\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.FAILED_TO_CONSTRUCT_STRIPE_EVENT,\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (!event) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.FAILED_TO_CONSTRUCT_STRIPE_EVENT,\n\t\t\t\t);\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tswitch (event.type) {\n\t\t\t\t\tcase \"checkout.session.completed\":\n\t\t\t\t\t\tawait onCheckoutSessionCompleted(ctx, options, event);\n\t\t\t\t\t\tawait options.onEvent?.(event);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"customer.subscription.created\":\n\t\t\t\t\t\tawait onSubscriptionCreated(ctx, options, event);\n\t\t\t\t\t\tawait options.onEvent?.(event);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"customer.subscription.updated\":\n\t\t\t\t\t\tawait onSubscriptionUpdated(ctx, options, event);\n\t\t\t\t\t\tawait options.onEvent?.(event);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"customer.subscription.deleted\":\n\t\t\t\t\t\tawait onSubscriptionDeleted(ctx, options, event);\n\t\t\t\t\t\tawait options.onEvent?.(event);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tawait options.onEvent?.(event);\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t} catch (e: any) {\n\t\t\t\tctx.context.logger.error(`Stripe webhook failed. Error: ${e.message}`);\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tSTRIPE_ERROR_CODES.STRIPE_WEBHOOK_ERROR,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn ctx.json({ success: true });\n\t\t},\n\t);\n};\n","import type { BetterAuthPluginDBSchema } from \"@better-auth/core/db\";\nimport { mergeSchema } from \"better-auth/db\";\nimport type { StripeOptions } from \"./types\";\n\nexport const subscriptions = {\n\tsubscription: {\n\t\tfields: {\n\t\t\tplan: {\n\t\t\t\ttype: \"string\",\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t\treferenceId: {\n\t\t\t\ttype: \"string\",\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t\tstripeCustomerId: {\n\t\t\t\ttype: \"string\",\n\t\t\t\trequired: false,\n\t\t\t},\n\t\t\tstripeSubscriptionId: {\n\t\t\t\ttype: \"string\",\n\t\t\t\trequired: false,\n\t\t\t},\n\t\t\tstatus: {\n\t\t\t\ttype: \"string\",\n\t\t\t\tdefaultValue: \"incomplete\",\n\t\t\t},\n\t\t\tperiodStart: {\n\t\t\t\ttype: \"date\",\n\t\t\t\trequired: false,\n\t\t\t},\n\t\t\tperiodEnd: {\n\t\t\t\ttype: \"date\",\n\t\t\t\trequired: false,\n\t\t\t},\n\t\t\ttrialStart: {\n\t\t\t\ttype: \"date\",\n\t\t\t\trequired: false,\n\t\t\t},\n\t\t\ttrialEnd: {\n\t\t\t\ttype: \"date\",\n\t\t\t\trequired: false,\n\t\t\t},\n\t\t\tcancelAtPeriodEnd: {\n\t\t\t\ttype: \"boolean\",\n\t\t\t\trequired: false,\n\t\t\t\tdefaultValue: false,\n\t\t\t},\n\t\t\tcancelAt: {\n\t\t\t\ttype: \"date\",\n\t\t\t\trequired: false,\n\t\t\t},\n\t\t\tcanceledAt: {\n\t\t\t\ttype: \"date\",\n\t\t\t\trequired: false,\n\t\t\t},\n\t\t\tendedAt: {\n\t\t\t\ttype: \"date\",\n\t\t\t\trequired: false,\n\t\t\t},\n\t\t\tseats: {\n\t\t\t\ttype: \"number\",\n\t\t\t\trequired: false,\n\t\t\t},\n\t\t\tbillingInterval: {\n\t\t\t\ttype: \"string\",\n\t\t\t\trequired: false,\n\t\t\t},\n\t\t\tstripeScheduleId: {\n\t\t\t\ttype: \"string\",\n\t\t\t\trequired: false,\n\t\t\t},\n\t\t},\n\t},\n} satisfies BetterAuthPluginDBSchema;\n\nexport const user = {\n\tuser: {\n\t\tfields: {\n\t\t\tstripeCustomerId: {\n\t\t\t\ttype: \"string\",\n\t\t\t\trequired: false,\n\t\t\t},\n\t\t},\n\t},\n} satisfies BetterAuthPluginDBSchema;\n\nexport const organization = {\n\torganization: {\n\t\tfields: {\n\t\t\tstripeCustomerId: {\n\t\t\t\ttype: \"string\",\n\t\t\t\trequired: false,\n\t\t\t},\n\t\t},\n\t},\n} satisfies BetterAuthPluginDBSchema;\n\ntype GetSchemaResult<O extends StripeOptions> = typeof user &\n\t(O[\"subscription\"] extends { enabled: true } ? typeof subscriptions : {}) &\n\t(O[\"organization\"] extends { enabled: true } ? typeof organization : {});\n\nexport const getSchema = <O extends StripeOptions>(\n\toptions: O,\n): GetSchemaResult<O> => {\n\tlet baseSchema: BetterAuthPluginDBSchema = {};\n\n\tif (options.subscription?.enabled) {\n\t\tbaseSchema = {\n\t\t\t...subscriptions,\n\t\t\t...user,\n\t\t};\n\t} else {\n\t\tbaseSchema = {\n\t\t\t...user,\n\t\t};\n\t}\n\n\tif (options.organization?.enabled) {\n\t\tbaseSchema = {\n\t\t\t...baseSchema,\n\t\t\t...organization,\n\t\t};\n\t}\n\n\tif (\n\t\toptions.schema &&\n\t\t!options.subscription?.enabled &&\n\t\t\"subscription\" in options.schema\n\t) {\n\t\tconst { subscription: _subscription, ...restSchema } = options.schema;\n\t\treturn mergeSchema(baseSchema, restSchema) as GetSchemaResult<O>;\n\t}\n\n\treturn mergeSchema(baseSchema, options.schema) as GetSchemaResult<O>;\n};\n","import type { BetterAuthPlugin, User } from \"better-auth\";\nimport { APIError } from \"better-auth\";\nimport type { Organization } from \"better-auth/plugins/organization\";\nimport { defu } from \"defu\";\nimport type Stripe from \"stripe\";\nimport { STRIPE_ERROR_CODES } from \"./error-codes\";\nimport { customerMetadata } from \"./metadata\";\nimport {\n\tcancelSubscription,\n\tcreateBillingPortal,\n\tlistActiveSubscriptions,\n\trestoreSubscription,\n\tstripeWebhook,\n\tsubscriptionSuccess,\n\tupgradeSubscription,\n} from \"./routes\";\nimport { getSchema } from \"./schema\";\nimport type {\n\tStripeOptions,\n\tStripePlan,\n\tSubscription,\n\tWithStripeCustomerId,\n} from \"./types\";\nimport { escapeStripeSearchValue, getPlans, isActiveOrTrialing } from \"./utils\";\n\ndeclare module \"@better-auth/core\" {\n\tinterface BetterAuthPluginRegistry<AuthOptions, Options> {\n\t\tstripe: {\n\t\t\tcreator: typeof stripe;\n\t\t};\n\t}\n}\n\nexport const stripe = <O extends StripeOptions>(options: O) => {\n\tconst client = options.stripeClient;\n\n\tconst subscriptionEndpoints = {\n\t\tupgradeSubscription: upgradeSubscription(options),\n\t\tcancelSubscription: cancelSubscription(options),\n\t\trestoreSubscription: restoreSubscription(options),\n\t\tlistActiveSubscriptions: listActiveSubscriptions(options),\n\t\tsubscriptionSuccess: subscriptionSuccess(options),\n\t\tcreateBillingPortal: createBillingPortal(options),\n\t};\n\n\treturn {\n\t\tid: \"stripe\",\n\t\tendpoints: {\n\t\t\tstripeWebhook: stripeWebhook(options),\n\t\t\t...((options.subscription?.enabled\n\t\t\t\t? subscriptionEndpoints\n\t\t\t\t: {}) as O[\"subscription\"] extends {\n\t\t\t\tenabled: true;\n\t\t\t}\n\t\t\t\t? typeof subscriptionEndpoints\n\t\t\t\t: {}),\n\t\t},\n\t\tinit(ctx) {\n\t\t\t// Validate: seatPriceId requires organization to be enabled\n\t\t\tif (options.subscription?.enabled && !options.organization?.enabled) {\n\t\t\t\tconst warnIfSeatPricing = (plans: StripePlan[]) => {\n\t\t\t\t\tif (plans.some((p) => p.seatPriceId)) {\n\t\t\t\t\t\tctx.logger.error(\n\t\t\t\t\t\t\t\"seatPriceId is configured on a plan but stripe organization option is not enabled. \" +\n\t\t\t\t\t\t\t\t\"Seat-based billing requires `organization: { enabled: true }` in stripe plugin options.\",\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t\tconst { plans } = options.subscription;\n\t\t\t\tif (typeof plans === \"function\") {\n\t\t\t\t\tvoid Promise.resolve(plans())\n\t\t\t\t\t\t.then(warnIfSeatPricing)\n\t\t\t\t\t\t.catch((e: any) => {\n\t\t\t\t\t\t\tctx.logger.error(\n\t\t\t\t\t\t\t\t`Failed to resolve plans for seat pricing validation: ${e.message}`,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t});\n\t\t\t\t} else {\n\t\t\t\t\twarnIfSeatPricing(plans);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (options.organization?.enabled) {\n\t\t\t\tconst orgPlugin = ctx.getPlugin(\"organization\");\n\t\t\t\tif (!orgPlugin) {\n\t\t\t\t\tctx.logger.error(`Organization plugin not found`);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tconst existingHooks = orgPlugin.options.organizationHooks ?? {};\n\n\t\t\t\t/**\n\t\t\t\t * Sync organization name to Stripe customer\n\t\t\t\t */\n\t\t\t\tconst afterUpdateStripeOrg = async (data: {\n\t\t\t\t\torganization: (Organization & WithStripeCustomerId) | null;\n\t\t\t\t\tuser: User;\n\t\t\t\t}) => {\n\t\t\t\t\tconst { organization } = data;\n\t\t\t\t\tif (!organization?.stripeCustomerId) return;\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst stripeCustomer = await client.customers.retrieve(\n\t\t\t\t\t\t\torganization.stripeCustomerId,\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\tif (stripeCustomer.deleted) {\n\t\t\t\t\t\t\tctx.logger.warn(\n\t\t\t\t\t\t\t\t`Stripe customer ${organization.stripeCustomerId} was deleted`,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Update Stripe customer if name changed\n\t\t\t\t\t\tif (organization.name !== stripeCustomer.name) {\n\t\t\t\t\t\t\tawait client.customers.update(organization.stripeCustomerId, {\n\t\t\t\t\t\t\t\tname: organization.name,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tctx.logger.info(\n\t\t\t\t\t\t\t\t`Synced organization name to Stripe: \"${stripeCustomer.name}\" → \"${organization.name}\"`,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (e: any) {\n\t\t\t\t\t\tctx.logger.error(\n\t\t\t\t\t\t\t`Failed to sync organization to Stripe: ${e.message}`,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\t/**\n\t\t\t\t * Block deletion if organization has active subscriptions\n\t\t\t\t */\n\t\t\t\tconst beforeDeleteStripeOrg = async (data: {\n\t\t\t\t\torganization: Organization & WithStripeCustomerId;\n\t\t\t\t\tuser: User;\n\t\t\t\t}) => {\n\t\t\t\t\tconst { organization } = data;\n\t\t\t\t\tif (!organization.stripeCustomerId) return;\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\t// Check if organization has any active subscriptions\n\t\t\t\t\t\tconst subscriptions = await client.subscriptions.list({\n\t\t\t\t\t\t\tcustomer: organization.stripeCustomerId,\n\t\t\t\t\t\t\tstatus: \"all\",\n\t\t\t\t\t\t\tlimit: 100, // 1 ~ 100\n\t\t\t\t\t\t});\n\t\t\t\t\t\tfor (const sub of subscriptions.data) {\n\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\tsub.status !== \"canceled\" &&\n\t\t\t\t\t\t\t\tsub.status !== \"incomplete\" &&\n\t\t\t\t\t\t\t\tsub.status !== \"incomplete_expired\"\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\t\t\t\t\tSTRIPE_ERROR_CODES.ORGANIZATION_HAS_ACTIVE_SUBSCRIPTION,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (error: any) {\n\t\t\t\t\t\tif (error instanceof APIError) {\n\t\t\t\t\t\t\tthrow error;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tctx.logger.error(\n\t\t\t\t\t\t\t`Failed to check organization subscriptions: ${error.message}`,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tthrow error;\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\t/**\n\t\t\t\t * Sync seat quantity to Stripe when organization members change.\n\t\t\t\t * quantity = memberCount; Stripe graduated pricing handles free tiers.\n\t\t\t\t */\n\t\t\t\tconst syncSeatsAfterMemberChange = async (data: {\n\t\t\t\t\torganization: Organization & WithStripeCustomerId;\n\t\t\t\t}) => {\n\t\t\t\t\tif (\n\t\t\t\t\t\t!options.subscription?.enabled ||\n\t\t\t\t\t\t!data.organization?.stripeCustomerId\n\t\t\t\t\t) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst memberCount = await ctx.adapter.count({\n\t\t\t\t\t\t\tmodel: \"member\",\n\t\t\t\t\t\t\twhere: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tfield: \"organizationId\",\n\t\t\t\t\t\t\t\t\tvalue: data.organization.id,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\tconst plans = await getPlans(options.subscription);\n\t\t\t\t\t\tconst seatPlans = plans.filter((p) => p.seatPriceId);\n\t\t\t\t\t\tif (seatPlans.length === 0) return;\n\n\t\t\t\t\t\tconst seatPlanNames = new Set(\n\t\t\t\t\t\t\tseatPlans.map((p) => p.name.toLowerCase()),\n\t\t\t\t\t\t);\n\t\t\t\t\t\tconst dbSub = await ctx.adapter.findOne<Subscription>({\n\t\t\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\t\t\twhere: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tfield: \"referenceId\",\n\t\t\t\t\t\t\t\t\tvalue: data.organization.id,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t});\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t!dbSub?.stripeSubscriptionId ||\n\t\t\t\t\t\t\t!isActiveOrTrialing(dbSub) ||\n\t\t\t\t\t\t\t!seatPlanNames.has(dbSub.plan)\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst plan = seatPlans.find(\n\t\t\t\t\t\t\t(p) => p.name.toLowerCase() === dbSub.plan,\n\t\t\t\t\t\t)!;\n\t\t\t\t\t\tconst { seatPriceId } = plan;\n\n\t\t\t\t\t\tconst stripeSub = await client.subscriptions.retrieve(\n\t\t\t\t\t\t\tdbSub.stripeSubscriptionId,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tif (!isActiveOrTrialing(stripeSub)) return;\n\n\t\t\t\t\t\tconst seatItem = stripeSub.items.data.find(\n\t\t\t\t\t\t\t(item) => item.price.id === seatPriceId,\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\t// Skip if no change needed\n\t\t\t\t\t\tif (seatItem?.quantity === memberCount) return;\n\n\t\t\t\t\t\tconst items = seatItem\n\t\t\t\t\t\t\t? [{ id: seatItem.id, quantity: memberCount }]\n\t\t\t\t\t\t\t: [{ price: seatPriceId, quantity: memberCount }];\n\n\t\t\t\t\t\tawait client.subscriptions.update(stripeSub.id, {\n\t\t\t\t\t\t\titems,\n\t\t\t\t\t\t\tproration_behavior: plan.prorationBehavior ?? \"create_prorations\",\n\t\t\t\t\t\t});\n\t\t\t\t\t\tawait ctx.adapter.update({\n\t\t\t\t\t\t\tmodel: \"subscription\",\n\t\t\t\t\t\t\tupdate: { seats: memberCount },\n\t\t\t\t\t\t\twhere: [{ field: \"id\", value: dbSub.id }],\n\t\t\t\t\t\t});\n\t\t\t\t\t} catch (e: any) {\n\t\t\t\t\t\tctx.logger.error(`Failed to sync seats to Stripe: ${e.message}`);\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\torgPlugin.options.organizationHooks = {\n\t\t\t\t\t...existingHooks,\n\t\t\t\t\tafterUpdateOrganization: existingHooks.afterUpdateOrganization\n\t\t\t\t\t\t? async (data) => {\n\t\t\t\t\t\t\t\tawait existingHooks.afterUpdateOrganization!(data);\n\t\t\t\t\t\t\t\tawait afterUpdateStripeOrg(data);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t: afterUpdateStripeOrg,\n\t\t\t\t\tbeforeDeleteOrganization: existingHooks.beforeDeleteOrganization\n\t\t\t\t\t\t? async (data) => {\n\t\t\t\t\t\t\t\tawait existingHooks.beforeDeleteOrganization!(data);\n\t\t\t\t\t\t\t\tawait beforeDeleteStripeOrg(data);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t: beforeDeleteStripeOrg,\n\t\t\t\t\tafterAddMember: existingHooks.afterAddMember\n\t\t\t\t\t\t? async (data) => {\n\t\t\t\t\t\t\t\tawait existingHooks.afterAddMember!(data);\n\t\t\t\t\t\t\t\tawait syncSeatsAfterMemberChange(data);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t: syncSeatsAfterMemberChange,\n\t\t\t\t\tafterRemoveMember: existingHooks.afterRemoveMember\n\t\t\t\t\t\t? async (data) => {\n\t\t\t\t\t\t\t\tawait existingHooks.afterRemoveMember!(data);\n\t\t\t\t\t\t\t\tawait syncSeatsAfterMemberChange(data);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t: syncSeatsAfterMemberChange,\n\t\t\t\t\tafterAcceptInvitation: existingHooks.afterAcceptInvitation\n\t\t\t\t\t\t? async (data) => {\n\t\t\t\t\t\t\t\tawait existingHooks.afterAcceptInvitation!(data);\n\t\t\t\t\t\t\t\tawait syncSeatsAfterMemberChange(data);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t: syncSeatsAfterMemberChange,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\toptions: {\n\t\t\t\t\tdatabaseHooks: {\n\t\t\t\t\t\tuser: {\n\t\t\t\t\t\t\tcreate: {\n\t\t\t\t\t\t\t\tasync after(user: User & WithStripeCustomerId, ctx) {\n\t\t\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t\t\t!ctx ||\n\t\t\t\t\t\t\t\t\t\t!options.createCustomerOnSignUp ||\n\t\t\t\t\t\t\t\t\t\tuser.stripeCustomerId // Skip if user already has a Stripe customer ID\n\t\t\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\t\t// Check if user customer already exists in Stripe by email\n\t\t\t\t\t\t\t\t\t\tlet stripeCustomer: Stripe.Customer | undefined;\n\t\t\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\t\t\tconst result = await client.customers.search({\n\t\t\t\t\t\t\t\t\t\t\t\tquery: `email:\"${escapeStripeSearchValue(user.email)}\" AND -metadata[\"${customerMetadata.keys.customerType}\"]:\"organization\"`,\n\t\t\t\t\t\t\t\t\t\t\t\tlimit: 1,\n\t\t\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\t\t\tstripeCustomer = result.data[0];\n\t\t\t\t\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t\t\t\t\t// Search API unavailable in some regions, so fall back to paginated list\n\t\t\t\t\t\t\t\t\t\t\tctx.context.logger.warn(\n\t\t\t\t\t\t\t\t\t\t\t\t\"Stripe customers.search failed, falling back to customers.list\",\n\t\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t\t\tfor await (const customer of client.customers.list({\n\t\t\t\t\t\t\t\t\t\t\t\temail: user.email,\n\t\t\t\t\t\t\t\t\t\t\t\tlimit: 100,\n\t\t\t\t\t\t\t\t\t\t\t})) {\n\t\t\t\t\t\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t\t\t\t\t\tcustomer.metadata?.[\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tcustomerMetadata.keys.customerType\n\t\t\t\t\t\t\t\t\t\t\t\t\t] !== \"organization\"\n\t\t\t\t\t\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\t\t\t\t\t\tstripeCustomer = customer;\n\t\t\t\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t\t// If user customer exists, link it to prevent duplicate creation\n\t\t\t\t\t\t\t\t\t\tif (stripeCustomer) {\n\t\t\t\t\t\t\t\t\t\t\tawait ctx.context.internalAdapter.updateUser(user.id, {\n\t\t\t\t\t\t\t\t\t\t\t\tstripeCustomerId: stripeCustomer.id,\n\t\t\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\t\t\tawait options.onCustomerCreate?.(\n\t\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\t\tstripeCustomer,\n\t\t\t\t\t\t\t\t\t\t\t\t\tuser: {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t...user,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tstripeCustomerId: stripeCustomer.id,\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\tctx,\n\t\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t\t\tctx.context.logger.info(\n\t\t\t\t\t\t\t\t\t\t\t\t`Linked existing Stripe customer ${stripeCustomer.id} to user ${user.id}`,\n\t\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t\t// Create new Stripe customer\n\t\t\t\t\t\t\t\t\t\tlet extraCreateParams: Partial<Stripe.CustomerCreateParams> =\n\t\t\t\t\t\t\t\t\t\t\t{};\n\t\t\t\t\t\t\t\t\t\tif (options.getCustomerCreateParams) {\n\t\t\t\t\t\t\t\t\t\t\textraCreateParams = await options.getCustomerCreateParams(\n\t\t\t\t\t\t\t\t\t\t\t\tuser,\n\t\t\t\t\t\t\t\t\t\t\t\tctx,\n\t\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t\tconst params = defu(\n\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\temail: user.email,\n\t\t\t\t\t\t\t\t\t\t\t\tname: user.name,\n\t\t\t\t\t\t\t\t\t\t\t\tmetadata: customerMetadata.set(\n\t\t\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tuserId: user.id,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tcustomerType: \"user\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\textraCreateParams?.metadata,\n\t\t\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\textraCreateParams,\n\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t\tstripeCustomer = await client.customers.create(params);\n\t\t\t\t\t\t\t\t\t\tawait ctx.context.internalAdapter.updateUser(user.id, {\n\t\t\t\t\t\t\t\t\t\t\tstripeCustomerId: stripeCustomer.id,\n\t\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\t\tawait options.onCustomerCreate?.(\n\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\tstripeCustomer,\n\t\t\t\t\t\t\t\t\t\t\t\tuser: {\n\t\t\t\t\t\t\t\t\t\t\t\t\t...user,\n\t\t\t\t\t\t\t\t\t\t\t\t\tstripeCustomerId: stripeCustomer.id,\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tctx,\n\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t\tctx.context.logger.info(\n\t\t\t\t\t\t\t\t\t\t\t`Created new Stripe customer ${stripeCustomer.id} for user ${user.id}`,\n\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t} catch (e: any) {\n\t\t\t\t\t\t\t\t\t\tctx.context.logger.error(\n\t\t\t\t\t\t\t\t\t\t\t`Failed to create or link Stripe customer: ${e.message}`,\n\t\t\t\t\t\t\t\t\t\t\te,\n\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tupdate: {\n\t\t\t\t\t\t\t\tasync after(user: User & WithStripeCustomerId, ctx) {\n\t\t\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t\t\t!ctx ||\n\t\t\t\t\t\t\t\t\t\t!user.stripeCustomerId // Only proceed if user has a Stripe customer ID\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\treturn;\n\n\t\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\t\t// Get the user from the database to check if email actually changed\n\t\t\t\t\t\t\t\t\t\t// The 'user' parameter here is the freshly updated user\n\t\t\t\t\t\t\t\t\t\t// We need to check if the Stripe customer's email matches\n\t\t\t\t\t\t\t\t\t\tconst stripeCustomer = await client.customers.retrieve(\n\t\t\t\t\t\t\t\t\t\t\tuser.stripeCustomerId,\n\t\t\t\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\t\t\t\t// Check if customer was deleted\n\t\t\t\t\t\t\t\t\t\tif (stripeCustomer.deleted) {\n\t\t\t\t\t\t\t\t\t\t\tctx.context.logger.warn(\n\t\t\t\t\t\t\t\t\t\t\t\t`Stripe customer ${user.stripeCustomerId} was deleted, cannot update email`,\n\t\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t\t// If Stripe customer email doesn't match the user's current email, update it\n\t\t\t\t\t\t\t\t\t\tif (stripeCustomer.email !== user.email) {\n\t\t\t\t\t\t\t\t\t\t\tawait client.customers.update(user.stripeCustomerId, {\n\t\t\t\t\t\t\t\t\t\t\t\temail: user.email,\n\t\t\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\t\t\tctx.context.logger.info(\n\t\t\t\t\t\t\t\t\t\t\t\t`Updated Stripe customer email from ${stripeCustomer.email} to ${user.email}`,\n\t\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t} catch (e: any) {\n\t\t\t\t\t\t\t\t\t\t// Ignore errors - this is a best-effort sync\n\t\t\t\t\t\t\t\t\t\t// Email might have been deleted or Stripe customer might not exist\n\t\t\t\t\t\t\t\t\t\tctx.context.logger.error(\n\t\t\t\t\t\t\t\t\t\t\t`Failed to sync email to Stripe customer: ${e.message}`,\n\t\t\t\t\t\t\t\t\t\t\te,\n\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t};\n\t\t},\n\t\tschema: getSchema(options),\n\t\toptions: options as NoInfer<O>,\n\t\t$ERROR_CODES: STRIPE_ERROR_CODES,\n\t} satisfies BetterAuthPlugin;\n};\n\nexport type StripePlugin<O extends StripeOptions> = ReturnType<\n\ttypeof stripe<O>\n>;\n\nexport type * from \"./types\";\n"],"mappings":"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"}
|