@better-auth/stripe 1.2.9-beta.8 → 1.2.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +4 -4
- package/dist/index.cjs +31 -4
- package/dist/index.d.cts +22 -22
- package/dist/index.d.mts +22 -22
- package/dist/index.d.ts +22 -22
- package/dist/index.mjs +31 -4
- package/package.json +2 -2
- package/src/index.ts +39 -6
- package/src/stripe.test.ts +117 -0
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
|
|
2
|
-
> @better-auth/stripe@1.2.9
|
|
2
|
+
> @better-auth/stripe@1.2.9 build /home/runner/work/better-auth/better-auth/packages/stripe
|
|
3
3
|
> unbuild
|
|
4
4
|
|
|
5
5
|
[info] Automatically detected entries: src/index, src/client [esm] [cjs] [dts]
|
|
6
6
|
[info] Building stripe
|
|
7
7
|
[success] Build succeeded for stripe
|
|
8
|
-
[log] dist/index.cjs (total size:
|
|
8
|
+
[log] dist/index.cjs (total size: 38.5 kB, chunk size: 38.5 kB, exports: stripe)
|
|
9
9
|
|
|
10
10
|
[log] dist/client.cjs (total size: 224 B, chunk size: 224 B, exports: stripeClient)
|
|
11
11
|
|
|
12
|
-
[log] dist/index.mjs (total size:
|
|
12
|
+
[log] dist/index.mjs (total size: 38.2 kB, chunk size: 38.2 kB, exports: stripe)
|
|
13
13
|
|
|
14
14
|
[log] dist/client.mjs (total size: 197 B, chunk size: 197 B, exports: stripeClient)
|
|
15
15
|
|
|
16
|
-
Σ Total dist size (byte size):
|
|
16
|
+
Σ Total dist size (byte size): 204 kB
|
|
17
17
|
[log]
|
package/dist/index.cjs
CHANGED
|
@@ -306,6 +306,15 @@ const getUrl = (ctx, url) => {
|
|
|
306
306
|
}
|
|
307
307
|
return `${ctx.context.options.baseURL}${url.startsWith("/") ? url : `/${url}`}`;
|
|
308
308
|
};
|
|
309
|
+
async function resolvePriceIdFromLookupKey(stripeClient, lookupKey) {
|
|
310
|
+
if (!lookupKey) return void 0;
|
|
311
|
+
const prices = await stripeClient.prices.list({
|
|
312
|
+
lookup_keys: [lookupKey],
|
|
313
|
+
active: true,
|
|
314
|
+
limit: 1
|
|
315
|
+
});
|
|
316
|
+
return prices.data[0]?.id;
|
|
317
|
+
}
|
|
309
318
|
const stripe = (options) => {
|
|
310
319
|
const client = options.stripeClient;
|
|
311
320
|
const referenceMiddleware = (action) => plugins.createAuthMiddleware(async (ctx) => {
|
|
@@ -483,7 +492,7 @@ const stripe = (options) => {
|
|
|
483
492
|
status: "active"
|
|
484
493
|
}).then(
|
|
485
494
|
(res) => res.data.find(
|
|
486
|
-
(subscription2) => subscription2.id === ctx.body.subscriptionId
|
|
495
|
+
(subscription2) => subscription2.id === subscriptionToUpdate?.stripeSubscriptionId || ctx.body.subscriptionId
|
|
487
496
|
)
|
|
488
497
|
).catch((e) => null) : null;
|
|
489
498
|
const subscriptions = subscriptionToUpdate ? [subscriptionToUpdate] : await ctx.context.adapter.findMany({
|
|
@@ -498,7 +507,7 @@ const stripe = (options) => {
|
|
|
498
507
|
const existingSubscription = subscriptions.find(
|
|
499
508
|
(sub) => sub.status === "active" || sub.status === "trialing"
|
|
500
509
|
);
|
|
501
|
-
if (existingSubscription && existingSubscription.status === "active" && existingSubscription.plan === ctx.body.plan) {
|
|
510
|
+
if (existingSubscription && existingSubscription.status === "active" && existingSubscription.plan === ctx.body.plan && existingSubscription.seats === (ctx.body.seats || 1)) {
|
|
502
511
|
throw new api.APIError("BAD_REQUEST", {
|
|
503
512
|
message: STRIPE_ERROR_CODES.ALREADY_SUBSCRIBED_PLAN
|
|
504
513
|
});
|
|
@@ -514,7 +523,7 @@ const stripe = (options) => {
|
|
|
514
523
|
items: [
|
|
515
524
|
{
|
|
516
525
|
id: activeSubscription.items.data[0]?.id,
|
|
517
|
-
quantity: 1,
|
|
526
|
+
quantity: ctx.body.seats || 1,
|
|
518
527
|
price: ctx.body.annual ? plan.annualDiscountPriceId : plan.priceId
|
|
519
528
|
}
|
|
520
529
|
]
|
|
@@ -561,6 +570,24 @@ const stripe = (options) => {
|
|
|
561
570
|
const freeTrail = plan.freeTrial ? {
|
|
562
571
|
trial_period_days: plan.freeTrial.days
|
|
563
572
|
} : void 0;
|
|
573
|
+
let priceIdToUse = void 0;
|
|
574
|
+
if (ctx.body.annual) {
|
|
575
|
+
priceIdToUse = plan.annualDiscountPriceId;
|
|
576
|
+
if (!priceIdToUse && plan.annualDiscountLookupKey) {
|
|
577
|
+
priceIdToUse = await resolvePriceIdFromLookupKey(
|
|
578
|
+
client,
|
|
579
|
+
plan.annualDiscountLookupKey
|
|
580
|
+
);
|
|
581
|
+
}
|
|
582
|
+
} else {
|
|
583
|
+
priceIdToUse = plan.priceId;
|
|
584
|
+
if (!priceIdToUse && plan.lookupKey) {
|
|
585
|
+
priceIdToUse = await resolvePriceIdFromLookupKey(
|
|
586
|
+
client,
|
|
587
|
+
plan.lookupKey
|
|
588
|
+
);
|
|
589
|
+
}
|
|
590
|
+
}
|
|
564
591
|
const checkoutSession = await client.checkout.sessions.create(
|
|
565
592
|
{
|
|
566
593
|
...customerId ? {
|
|
@@ -581,7 +608,7 @@ const stripe = (options) => {
|
|
|
581
608
|
cancel_url: getUrl(ctx, ctx.body.cancelUrl),
|
|
582
609
|
line_items: [
|
|
583
610
|
{
|
|
584
|
-
price:
|
|
611
|
+
price: priceIdToUse,
|
|
585
612
|
quantity: ctx.body.seats || 1
|
|
586
613
|
}
|
|
587
614
|
],
|
package/dist/index.d.cts
CHANGED
|
@@ -656,19 +656,19 @@ declare const stripe: <O extends StripeOptions>(options: O) => {
|
|
|
656
656
|
session: {
|
|
657
657
|
session: Record<string, any> & {
|
|
658
658
|
id: string;
|
|
659
|
-
createdAt: Date;
|
|
660
|
-
updatedAt: Date;
|
|
661
659
|
token: string;
|
|
662
|
-
expiresAt: Date;
|
|
663
660
|
userId: string;
|
|
661
|
+
expiresAt: Date;
|
|
662
|
+
createdAt: Date;
|
|
663
|
+
updatedAt: Date;
|
|
664
664
|
ipAddress?: string | null | undefined;
|
|
665
665
|
userAgent?: string | null | undefined;
|
|
666
666
|
};
|
|
667
667
|
user: Record<string, any> & {
|
|
668
668
|
id: string;
|
|
669
|
-
email: string;
|
|
670
|
-
emailVerified: boolean;
|
|
671
669
|
name: string;
|
|
670
|
+
emailVerified: boolean;
|
|
671
|
+
email: string;
|
|
672
672
|
createdAt: Date;
|
|
673
673
|
updatedAt: Date;
|
|
674
674
|
image?: string | null | undefined;
|
|
@@ -768,19 +768,19 @@ declare const stripe: <O extends StripeOptions>(options: O) => {
|
|
|
768
768
|
session: {
|
|
769
769
|
session: Record<string, any> & {
|
|
770
770
|
id: string;
|
|
771
|
-
createdAt: Date;
|
|
772
|
-
updatedAt: Date;
|
|
773
771
|
token: string;
|
|
774
|
-
expiresAt: Date;
|
|
775
772
|
userId: string;
|
|
773
|
+
expiresAt: Date;
|
|
774
|
+
createdAt: Date;
|
|
775
|
+
updatedAt: Date;
|
|
776
776
|
ipAddress?: string | null | undefined;
|
|
777
777
|
userAgent?: string | null | undefined;
|
|
778
778
|
};
|
|
779
779
|
user: Record<string, any> & {
|
|
780
780
|
id: string;
|
|
781
|
-
email: string;
|
|
782
|
-
emailVerified: boolean;
|
|
783
781
|
name: string;
|
|
782
|
+
emailVerified: boolean;
|
|
783
|
+
email: string;
|
|
784
784
|
createdAt: Date;
|
|
785
785
|
updatedAt: Date;
|
|
786
786
|
image?: string | null | undefined;
|
|
@@ -836,19 +836,19 @@ declare const stripe: <O extends StripeOptions>(options: O) => {
|
|
|
836
836
|
session: {
|
|
837
837
|
session: Record<string, any> & {
|
|
838
838
|
id: string;
|
|
839
|
-
createdAt: Date;
|
|
840
|
-
updatedAt: Date;
|
|
841
839
|
token: string;
|
|
842
|
-
expiresAt: Date;
|
|
843
840
|
userId: string;
|
|
841
|
+
expiresAt: Date;
|
|
842
|
+
createdAt: Date;
|
|
843
|
+
updatedAt: Date;
|
|
844
844
|
ipAddress?: string | null | undefined;
|
|
845
845
|
userAgent?: string | null | undefined;
|
|
846
846
|
};
|
|
847
847
|
user: Record<string, any> & {
|
|
848
848
|
id: string;
|
|
849
|
-
email: string;
|
|
850
|
-
emailVerified: boolean;
|
|
851
849
|
name: string;
|
|
850
|
+
emailVerified: boolean;
|
|
851
|
+
email: string;
|
|
852
852
|
createdAt: Date;
|
|
853
853
|
updatedAt: Date;
|
|
854
854
|
image?: string | null | undefined;
|
|
@@ -932,19 +932,19 @@ declare const stripe: <O extends StripeOptions>(options: O) => {
|
|
|
932
932
|
session: {
|
|
933
933
|
session: Record<string, any> & {
|
|
934
934
|
id: string;
|
|
935
|
-
createdAt: Date;
|
|
936
|
-
updatedAt: Date;
|
|
937
935
|
token: string;
|
|
938
|
-
expiresAt: Date;
|
|
939
936
|
userId: string;
|
|
937
|
+
expiresAt: Date;
|
|
938
|
+
createdAt: Date;
|
|
939
|
+
updatedAt: Date;
|
|
940
940
|
ipAddress?: string | null | undefined;
|
|
941
941
|
userAgent?: string | null | undefined;
|
|
942
942
|
};
|
|
943
943
|
user: Record<string, any> & {
|
|
944
944
|
id: string;
|
|
945
|
-
email: string;
|
|
946
|
-
emailVerified: boolean;
|
|
947
945
|
name: string;
|
|
946
|
+
emailVerified: boolean;
|
|
947
|
+
email: string;
|
|
948
948
|
createdAt: Date;
|
|
949
949
|
updatedAt: Date;
|
|
950
950
|
image?: string | null | undefined;
|
|
@@ -998,9 +998,9 @@ declare const stripe: <O extends StripeOptions>(options: O) => {
|
|
|
998
998
|
create: {
|
|
999
999
|
after(user: {
|
|
1000
1000
|
id: string;
|
|
1001
|
-
email: string;
|
|
1002
|
-
emailVerified: boolean;
|
|
1003
1001
|
name: string;
|
|
1002
|
+
emailVerified: boolean;
|
|
1003
|
+
email: string;
|
|
1004
1004
|
createdAt: Date;
|
|
1005
1005
|
updatedAt: Date;
|
|
1006
1006
|
image?: string | null | undefined;
|
package/dist/index.d.mts
CHANGED
|
@@ -656,19 +656,19 @@ declare const stripe: <O extends StripeOptions>(options: O) => {
|
|
|
656
656
|
session: {
|
|
657
657
|
session: Record<string, any> & {
|
|
658
658
|
id: string;
|
|
659
|
-
createdAt: Date;
|
|
660
|
-
updatedAt: Date;
|
|
661
659
|
token: string;
|
|
662
|
-
expiresAt: Date;
|
|
663
660
|
userId: string;
|
|
661
|
+
expiresAt: Date;
|
|
662
|
+
createdAt: Date;
|
|
663
|
+
updatedAt: Date;
|
|
664
664
|
ipAddress?: string | null | undefined;
|
|
665
665
|
userAgent?: string | null | undefined;
|
|
666
666
|
};
|
|
667
667
|
user: Record<string, any> & {
|
|
668
668
|
id: string;
|
|
669
|
-
email: string;
|
|
670
|
-
emailVerified: boolean;
|
|
671
669
|
name: string;
|
|
670
|
+
emailVerified: boolean;
|
|
671
|
+
email: string;
|
|
672
672
|
createdAt: Date;
|
|
673
673
|
updatedAt: Date;
|
|
674
674
|
image?: string | null | undefined;
|
|
@@ -768,19 +768,19 @@ declare const stripe: <O extends StripeOptions>(options: O) => {
|
|
|
768
768
|
session: {
|
|
769
769
|
session: Record<string, any> & {
|
|
770
770
|
id: string;
|
|
771
|
-
createdAt: Date;
|
|
772
|
-
updatedAt: Date;
|
|
773
771
|
token: string;
|
|
774
|
-
expiresAt: Date;
|
|
775
772
|
userId: string;
|
|
773
|
+
expiresAt: Date;
|
|
774
|
+
createdAt: Date;
|
|
775
|
+
updatedAt: Date;
|
|
776
776
|
ipAddress?: string | null | undefined;
|
|
777
777
|
userAgent?: string | null | undefined;
|
|
778
778
|
};
|
|
779
779
|
user: Record<string, any> & {
|
|
780
780
|
id: string;
|
|
781
|
-
email: string;
|
|
782
|
-
emailVerified: boolean;
|
|
783
781
|
name: string;
|
|
782
|
+
emailVerified: boolean;
|
|
783
|
+
email: string;
|
|
784
784
|
createdAt: Date;
|
|
785
785
|
updatedAt: Date;
|
|
786
786
|
image?: string | null | undefined;
|
|
@@ -836,19 +836,19 @@ declare const stripe: <O extends StripeOptions>(options: O) => {
|
|
|
836
836
|
session: {
|
|
837
837
|
session: Record<string, any> & {
|
|
838
838
|
id: string;
|
|
839
|
-
createdAt: Date;
|
|
840
|
-
updatedAt: Date;
|
|
841
839
|
token: string;
|
|
842
|
-
expiresAt: Date;
|
|
843
840
|
userId: string;
|
|
841
|
+
expiresAt: Date;
|
|
842
|
+
createdAt: Date;
|
|
843
|
+
updatedAt: Date;
|
|
844
844
|
ipAddress?: string | null | undefined;
|
|
845
845
|
userAgent?: string | null | undefined;
|
|
846
846
|
};
|
|
847
847
|
user: Record<string, any> & {
|
|
848
848
|
id: string;
|
|
849
|
-
email: string;
|
|
850
|
-
emailVerified: boolean;
|
|
851
849
|
name: string;
|
|
850
|
+
emailVerified: boolean;
|
|
851
|
+
email: string;
|
|
852
852
|
createdAt: Date;
|
|
853
853
|
updatedAt: Date;
|
|
854
854
|
image?: string | null | undefined;
|
|
@@ -932,19 +932,19 @@ declare const stripe: <O extends StripeOptions>(options: O) => {
|
|
|
932
932
|
session: {
|
|
933
933
|
session: Record<string, any> & {
|
|
934
934
|
id: string;
|
|
935
|
-
createdAt: Date;
|
|
936
|
-
updatedAt: Date;
|
|
937
935
|
token: string;
|
|
938
|
-
expiresAt: Date;
|
|
939
936
|
userId: string;
|
|
937
|
+
expiresAt: Date;
|
|
938
|
+
createdAt: Date;
|
|
939
|
+
updatedAt: Date;
|
|
940
940
|
ipAddress?: string | null | undefined;
|
|
941
941
|
userAgent?: string | null | undefined;
|
|
942
942
|
};
|
|
943
943
|
user: Record<string, any> & {
|
|
944
944
|
id: string;
|
|
945
|
-
email: string;
|
|
946
|
-
emailVerified: boolean;
|
|
947
945
|
name: string;
|
|
946
|
+
emailVerified: boolean;
|
|
947
|
+
email: string;
|
|
948
948
|
createdAt: Date;
|
|
949
949
|
updatedAt: Date;
|
|
950
950
|
image?: string | null | undefined;
|
|
@@ -998,9 +998,9 @@ declare const stripe: <O extends StripeOptions>(options: O) => {
|
|
|
998
998
|
create: {
|
|
999
999
|
after(user: {
|
|
1000
1000
|
id: string;
|
|
1001
|
-
email: string;
|
|
1002
|
-
emailVerified: boolean;
|
|
1003
1001
|
name: string;
|
|
1002
|
+
emailVerified: boolean;
|
|
1003
|
+
email: string;
|
|
1004
1004
|
createdAt: Date;
|
|
1005
1005
|
updatedAt: Date;
|
|
1006
1006
|
image?: string | null | undefined;
|
package/dist/index.d.ts
CHANGED
|
@@ -656,19 +656,19 @@ declare const stripe: <O extends StripeOptions>(options: O) => {
|
|
|
656
656
|
session: {
|
|
657
657
|
session: Record<string, any> & {
|
|
658
658
|
id: string;
|
|
659
|
-
createdAt: Date;
|
|
660
|
-
updatedAt: Date;
|
|
661
659
|
token: string;
|
|
662
|
-
expiresAt: Date;
|
|
663
660
|
userId: string;
|
|
661
|
+
expiresAt: Date;
|
|
662
|
+
createdAt: Date;
|
|
663
|
+
updatedAt: Date;
|
|
664
664
|
ipAddress?: string | null | undefined;
|
|
665
665
|
userAgent?: string | null | undefined;
|
|
666
666
|
};
|
|
667
667
|
user: Record<string, any> & {
|
|
668
668
|
id: string;
|
|
669
|
-
email: string;
|
|
670
|
-
emailVerified: boolean;
|
|
671
669
|
name: string;
|
|
670
|
+
emailVerified: boolean;
|
|
671
|
+
email: string;
|
|
672
672
|
createdAt: Date;
|
|
673
673
|
updatedAt: Date;
|
|
674
674
|
image?: string | null | undefined;
|
|
@@ -768,19 +768,19 @@ declare const stripe: <O extends StripeOptions>(options: O) => {
|
|
|
768
768
|
session: {
|
|
769
769
|
session: Record<string, any> & {
|
|
770
770
|
id: string;
|
|
771
|
-
createdAt: Date;
|
|
772
|
-
updatedAt: Date;
|
|
773
771
|
token: string;
|
|
774
|
-
expiresAt: Date;
|
|
775
772
|
userId: string;
|
|
773
|
+
expiresAt: Date;
|
|
774
|
+
createdAt: Date;
|
|
775
|
+
updatedAt: Date;
|
|
776
776
|
ipAddress?: string | null | undefined;
|
|
777
777
|
userAgent?: string | null | undefined;
|
|
778
778
|
};
|
|
779
779
|
user: Record<string, any> & {
|
|
780
780
|
id: string;
|
|
781
|
-
email: string;
|
|
782
|
-
emailVerified: boolean;
|
|
783
781
|
name: string;
|
|
782
|
+
emailVerified: boolean;
|
|
783
|
+
email: string;
|
|
784
784
|
createdAt: Date;
|
|
785
785
|
updatedAt: Date;
|
|
786
786
|
image?: string | null | undefined;
|
|
@@ -836,19 +836,19 @@ declare const stripe: <O extends StripeOptions>(options: O) => {
|
|
|
836
836
|
session: {
|
|
837
837
|
session: Record<string, any> & {
|
|
838
838
|
id: string;
|
|
839
|
-
createdAt: Date;
|
|
840
|
-
updatedAt: Date;
|
|
841
839
|
token: string;
|
|
842
|
-
expiresAt: Date;
|
|
843
840
|
userId: string;
|
|
841
|
+
expiresAt: Date;
|
|
842
|
+
createdAt: Date;
|
|
843
|
+
updatedAt: Date;
|
|
844
844
|
ipAddress?: string | null | undefined;
|
|
845
845
|
userAgent?: string | null | undefined;
|
|
846
846
|
};
|
|
847
847
|
user: Record<string, any> & {
|
|
848
848
|
id: string;
|
|
849
|
-
email: string;
|
|
850
|
-
emailVerified: boolean;
|
|
851
849
|
name: string;
|
|
850
|
+
emailVerified: boolean;
|
|
851
|
+
email: string;
|
|
852
852
|
createdAt: Date;
|
|
853
853
|
updatedAt: Date;
|
|
854
854
|
image?: string | null | undefined;
|
|
@@ -932,19 +932,19 @@ declare const stripe: <O extends StripeOptions>(options: O) => {
|
|
|
932
932
|
session: {
|
|
933
933
|
session: Record<string, any> & {
|
|
934
934
|
id: string;
|
|
935
|
-
createdAt: Date;
|
|
936
|
-
updatedAt: Date;
|
|
937
935
|
token: string;
|
|
938
|
-
expiresAt: Date;
|
|
939
936
|
userId: string;
|
|
937
|
+
expiresAt: Date;
|
|
938
|
+
createdAt: Date;
|
|
939
|
+
updatedAt: Date;
|
|
940
940
|
ipAddress?: string | null | undefined;
|
|
941
941
|
userAgent?: string | null | undefined;
|
|
942
942
|
};
|
|
943
943
|
user: Record<string, any> & {
|
|
944
944
|
id: string;
|
|
945
|
-
email: string;
|
|
946
|
-
emailVerified: boolean;
|
|
947
945
|
name: string;
|
|
946
|
+
emailVerified: boolean;
|
|
947
|
+
email: string;
|
|
948
948
|
createdAt: Date;
|
|
949
949
|
updatedAt: Date;
|
|
950
950
|
image?: string | null | undefined;
|
|
@@ -998,9 +998,9 @@ declare const stripe: <O extends StripeOptions>(options: O) => {
|
|
|
998
998
|
create: {
|
|
999
999
|
after(user: {
|
|
1000
1000
|
id: string;
|
|
1001
|
-
email: string;
|
|
1002
|
-
emailVerified: boolean;
|
|
1003
1001
|
name: string;
|
|
1002
|
+
emailVerified: boolean;
|
|
1003
|
+
email: string;
|
|
1004
1004
|
createdAt: Date;
|
|
1005
1005
|
updatedAt: Date;
|
|
1006
1006
|
image?: string | null | undefined;
|
package/dist/index.mjs
CHANGED
|
@@ -304,6 +304,15 @@ const getUrl = (ctx, url) => {
|
|
|
304
304
|
}
|
|
305
305
|
return `${ctx.context.options.baseURL}${url.startsWith("/") ? url : `/${url}`}`;
|
|
306
306
|
};
|
|
307
|
+
async function resolvePriceIdFromLookupKey(stripeClient, lookupKey) {
|
|
308
|
+
if (!lookupKey) return void 0;
|
|
309
|
+
const prices = await stripeClient.prices.list({
|
|
310
|
+
lookup_keys: [lookupKey],
|
|
311
|
+
active: true,
|
|
312
|
+
limit: 1
|
|
313
|
+
});
|
|
314
|
+
return prices.data[0]?.id;
|
|
315
|
+
}
|
|
307
316
|
const stripe = (options) => {
|
|
308
317
|
const client = options.stripeClient;
|
|
309
318
|
const referenceMiddleware = (action) => createAuthMiddleware(async (ctx) => {
|
|
@@ -481,7 +490,7 @@ const stripe = (options) => {
|
|
|
481
490
|
status: "active"
|
|
482
491
|
}).then(
|
|
483
492
|
(res) => res.data.find(
|
|
484
|
-
(subscription2) => subscription2.id === ctx.body.subscriptionId
|
|
493
|
+
(subscription2) => subscription2.id === subscriptionToUpdate?.stripeSubscriptionId || ctx.body.subscriptionId
|
|
485
494
|
)
|
|
486
495
|
).catch((e) => null) : null;
|
|
487
496
|
const subscriptions = subscriptionToUpdate ? [subscriptionToUpdate] : await ctx.context.adapter.findMany({
|
|
@@ -496,7 +505,7 @@ const stripe = (options) => {
|
|
|
496
505
|
const existingSubscription = subscriptions.find(
|
|
497
506
|
(sub) => sub.status === "active" || sub.status === "trialing"
|
|
498
507
|
);
|
|
499
|
-
if (existingSubscription && existingSubscription.status === "active" && existingSubscription.plan === ctx.body.plan) {
|
|
508
|
+
if (existingSubscription && existingSubscription.status === "active" && existingSubscription.plan === ctx.body.plan && existingSubscription.seats === (ctx.body.seats || 1)) {
|
|
500
509
|
throw new APIError("BAD_REQUEST", {
|
|
501
510
|
message: STRIPE_ERROR_CODES.ALREADY_SUBSCRIBED_PLAN
|
|
502
511
|
});
|
|
@@ -512,7 +521,7 @@ const stripe = (options) => {
|
|
|
512
521
|
items: [
|
|
513
522
|
{
|
|
514
523
|
id: activeSubscription.items.data[0]?.id,
|
|
515
|
-
quantity: 1,
|
|
524
|
+
quantity: ctx.body.seats || 1,
|
|
516
525
|
price: ctx.body.annual ? plan.annualDiscountPriceId : plan.priceId
|
|
517
526
|
}
|
|
518
527
|
]
|
|
@@ -559,6 +568,24 @@ const stripe = (options) => {
|
|
|
559
568
|
const freeTrail = plan.freeTrial ? {
|
|
560
569
|
trial_period_days: plan.freeTrial.days
|
|
561
570
|
} : void 0;
|
|
571
|
+
let priceIdToUse = void 0;
|
|
572
|
+
if (ctx.body.annual) {
|
|
573
|
+
priceIdToUse = plan.annualDiscountPriceId;
|
|
574
|
+
if (!priceIdToUse && plan.annualDiscountLookupKey) {
|
|
575
|
+
priceIdToUse = await resolvePriceIdFromLookupKey(
|
|
576
|
+
client,
|
|
577
|
+
plan.annualDiscountLookupKey
|
|
578
|
+
);
|
|
579
|
+
}
|
|
580
|
+
} else {
|
|
581
|
+
priceIdToUse = plan.priceId;
|
|
582
|
+
if (!priceIdToUse && plan.lookupKey) {
|
|
583
|
+
priceIdToUse = await resolvePriceIdFromLookupKey(
|
|
584
|
+
client,
|
|
585
|
+
plan.lookupKey
|
|
586
|
+
);
|
|
587
|
+
}
|
|
588
|
+
}
|
|
562
589
|
const checkoutSession = await client.checkout.sessions.create(
|
|
563
590
|
{
|
|
564
591
|
...customerId ? {
|
|
@@ -579,7 +606,7 @@ const stripe = (options) => {
|
|
|
579
606
|
cancel_url: getUrl(ctx, ctx.body.cancelUrl),
|
|
580
607
|
line_items: [
|
|
581
608
|
{
|
|
582
|
-
price:
|
|
609
|
+
price: priceIdToUse,
|
|
583
610
|
quantity: ctx.body.seats || 1
|
|
584
611
|
}
|
|
585
612
|
],
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@better-auth/stripe",
|
|
3
3
|
"author": "Bereket Engida",
|
|
4
|
-
"version": "1.2.9
|
|
4
|
+
"version": "1.2.9",
|
|
5
5
|
"main": "dist/index.cjs",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"keywords": [
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
37
|
"zod": "^3.24.1",
|
|
38
|
-
"better-auth": "^1.2.9
|
|
38
|
+
"better-auth": "^1.2.9"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@types/better-sqlite3": "^7.6.12",
|
package/src/index.ts
CHANGED
|
@@ -50,6 +50,19 @@ const getUrl = (ctx: GenericEndpointContext, url: string) => {
|
|
|
50
50
|
}`;
|
|
51
51
|
};
|
|
52
52
|
|
|
53
|
+
async function resolvePriceIdFromLookupKey(
|
|
54
|
+
stripeClient: Stripe,
|
|
55
|
+
lookupKey: string,
|
|
56
|
+
): Promise<string | undefined> {
|
|
57
|
+
if (!lookupKey) return undefined;
|
|
58
|
+
const prices = await stripeClient.prices.list({
|
|
59
|
+
lookup_keys: [lookupKey],
|
|
60
|
+
active: true,
|
|
61
|
+
limit: 1,
|
|
62
|
+
});
|
|
63
|
+
return prices.data[0]?.id;
|
|
64
|
+
}
|
|
65
|
+
|
|
53
66
|
export const stripe = <O extends StripeOptions>(options: O) => {
|
|
54
67
|
const client = options.stripeClient;
|
|
55
68
|
|
|
@@ -268,7 +281,10 @@ export const stripe = <O extends StripeOptions>(options: O) => {
|
|
|
268
281
|
})
|
|
269
282
|
.then((res) =>
|
|
270
283
|
res.data.find(
|
|
271
|
-
(subscription) =>
|
|
284
|
+
(subscription) =>
|
|
285
|
+
subscription.id ===
|
|
286
|
+
subscriptionToUpdate?.stripeSubscriptionId ||
|
|
287
|
+
ctx.body.subscriptionId,
|
|
272
288
|
),
|
|
273
289
|
)
|
|
274
290
|
.catch((e) => null)
|
|
@@ -293,7 +309,8 @@ export const stripe = <O extends StripeOptions>(options: O) => {
|
|
|
293
309
|
if (
|
|
294
310
|
existingSubscription &&
|
|
295
311
|
existingSubscription.status === "active" &&
|
|
296
|
-
existingSubscription.plan === ctx.body.plan
|
|
312
|
+
existingSubscription.plan === ctx.body.plan &&
|
|
313
|
+
existingSubscription.seats === (ctx.body.seats || 1)
|
|
297
314
|
) {
|
|
298
315
|
throw new APIError("BAD_REQUEST", {
|
|
299
316
|
message: STRIPE_ERROR_CODES.ALREADY_SUBSCRIBED_PLAN,
|
|
@@ -312,7 +329,7 @@ export const stripe = <O extends StripeOptions>(options: O) => {
|
|
|
312
329
|
items: [
|
|
313
330
|
{
|
|
314
331
|
id: activeSubscription.items.data[0]?.id as string,
|
|
315
|
-
quantity: 1,
|
|
332
|
+
quantity: ctx.body.seats || 1,
|
|
316
333
|
price: ctx.body.annual
|
|
317
334
|
? plan.annualDiscountPriceId
|
|
318
335
|
: plan.priceId,
|
|
@@ -372,6 +389,24 @@ export const stripe = <O extends StripeOptions>(options: O) => {
|
|
|
372
389
|
}
|
|
373
390
|
: undefined;
|
|
374
391
|
|
|
392
|
+
let priceIdToUse: string | undefined = undefined;
|
|
393
|
+
if (ctx.body.annual) {
|
|
394
|
+
priceIdToUse = plan.annualDiscountPriceId;
|
|
395
|
+
if (!priceIdToUse && plan.annualDiscountLookupKey) {
|
|
396
|
+
priceIdToUse = await resolvePriceIdFromLookupKey(
|
|
397
|
+
client,
|
|
398
|
+
plan.annualDiscountLookupKey,
|
|
399
|
+
);
|
|
400
|
+
}
|
|
401
|
+
} else {
|
|
402
|
+
priceIdToUse = plan.priceId;
|
|
403
|
+
if (!priceIdToUse && plan.lookupKey) {
|
|
404
|
+
priceIdToUse = await resolvePriceIdFromLookupKey(
|
|
405
|
+
client,
|
|
406
|
+
plan.lookupKey,
|
|
407
|
+
);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
375
410
|
const checkoutSession = await client.checkout.sessions
|
|
376
411
|
.create(
|
|
377
412
|
{
|
|
@@ -397,9 +432,7 @@ export const stripe = <O extends StripeOptions>(options: O) => {
|
|
|
397
432
|
cancel_url: getUrl(ctx, ctx.body.cancelUrl),
|
|
398
433
|
line_items: [
|
|
399
434
|
{
|
|
400
|
-
price:
|
|
401
|
-
? plan.annualDiscountPriceId
|
|
402
|
-
: plan.priceId,
|
|
435
|
+
price: priceIdToUse,
|
|
403
436
|
quantity: ctx.body.seats || 1,
|
|
404
437
|
},
|
|
405
438
|
],
|
package/src/stripe.test.ts
CHANGED
|
@@ -11,6 +11,9 @@ import type { StripeOptions, Subscription } from "./types";
|
|
|
11
11
|
|
|
12
12
|
describe("stripe", async () => {
|
|
13
13
|
const mockStripe = {
|
|
14
|
+
prices: {
|
|
15
|
+
list: vi.fn().mockResolvedValue({ data: [{ id: "price_lookup_123" }] }),
|
|
16
|
+
},
|
|
14
17
|
customers: {
|
|
15
18
|
create: vi.fn().mockResolvedValue({ id: "cus_mock123" }),
|
|
16
19
|
},
|
|
@@ -59,10 +62,12 @@ describe("stripe", async () => {
|
|
|
59
62
|
{
|
|
60
63
|
priceId: process.env.STRIPE_PRICE_ID_1!,
|
|
61
64
|
name: "starter",
|
|
65
|
+
lookupKey: "lookup_key_123",
|
|
62
66
|
},
|
|
63
67
|
{
|
|
64
68
|
priceId: process.env.STRIPE_PRICE_ID_2!,
|
|
65
69
|
name: "premium",
|
|
70
|
+
lookupKey: "lookup_key_234",
|
|
66
71
|
},
|
|
67
72
|
],
|
|
68
73
|
},
|
|
@@ -700,4 +705,116 @@ describe("stripe", async () => {
|
|
|
700
705
|
|
|
701
706
|
expect(onSubscriptionDeleted).toHaveBeenCalled();
|
|
702
707
|
});
|
|
708
|
+
|
|
709
|
+
it("should allow seat upgrades for the same plan", async () => {
|
|
710
|
+
const userRes = await authClient.signUp.email(
|
|
711
|
+
{
|
|
712
|
+
...testUser,
|
|
713
|
+
email: "seat-upgrade@email.com",
|
|
714
|
+
},
|
|
715
|
+
{
|
|
716
|
+
throw: true,
|
|
717
|
+
},
|
|
718
|
+
);
|
|
719
|
+
|
|
720
|
+
const headers = new Headers();
|
|
721
|
+
await authClient.signIn.email(
|
|
722
|
+
{
|
|
723
|
+
...testUser,
|
|
724
|
+
email: "seat-upgrade@email.com",
|
|
725
|
+
},
|
|
726
|
+
{
|
|
727
|
+
throw: true,
|
|
728
|
+
onSuccess: setCookieToHeader(headers),
|
|
729
|
+
},
|
|
730
|
+
);
|
|
731
|
+
|
|
732
|
+
await authClient.subscription.upgrade({
|
|
733
|
+
plan: "starter",
|
|
734
|
+
seats: 1,
|
|
735
|
+
fetchOptions: {
|
|
736
|
+
headers,
|
|
737
|
+
},
|
|
738
|
+
});
|
|
739
|
+
|
|
740
|
+
await ctx.adapter.update({
|
|
741
|
+
model: "subscription",
|
|
742
|
+
update: {
|
|
743
|
+
status: "active",
|
|
744
|
+
},
|
|
745
|
+
where: [
|
|
746
|
+
{
|
|
747
|
+
field: "referenceId",
|
|
748
|
+
value: userRes.user.id,
|
|
749
|
+
},
|
|
750
|
+
],
|
|
751
|
+
});
|
|
752
|
+
|
|
753
|
+
const upgradeRes = await authClient.subscription.upgrade({
|
|
754
|
+
plan: "starter",
|
|
755
|
+
seats: 5,
|
|
756
|
+
fetchOptions: {
|
|
757
|
+
headers,
|
|
758
|
+
},
|
|
759
|
+
});
|
|
760
|
+
|
|
761
|
+
expect(upgradeRes.data?.url).toBeDefined();
|
|
762
|
+
});
|
|
763
|
+
|
|
764
|
+
it("should prevent duplicate subscriptions with same plan and same seats", async () => {
|
|
765
|
+
const userRes = await authClient.signUp.email(
|
|
766
|
+
{
|
|
767
|
+
...testUser,
|
|
768
|
+
email: "duplicate-prevention@email.com",
|
|
769
|
+
},
|
|
770
|
+
{
|
|
771
|
+
throw: true,
|
|
772
|
+
},
|
|
773
|
+
);
|
|
774
|
+
|
|
775
|
+
const headers = new Headers();
|
|
776
|
+
await authClient.signIn.email(
|
|
777
|
+
{
|
|
778
|
+
...testUser,
|
|
779
|
+
email: "duplicate-prevention@email.com",
|
|
780
|
+
},
|
|
781
|
+
{
|
|
782
|
+
throw: true,
|
|
783
|
+
onSuccess: setCookieToHeader(headers),
|
|
784
|
+
},
|
|
785
|
+
);
|
|
786
|
+
|
|
787
|
+
await authClient.subscription.upgrade({
|
|
788
|
+
plan: "starter",
|
|
789
|
+
seats: 3,
|
|
790
|
+
fetchOptions: {
|
|
791
|
+
headers,
|
|
792
|
+
},
|
|
793
|
+
});
|
|
794
|
+
|
|
795
|
+
await ctx.adapter.update({
|
|
796
|
+
model: "subscription",
|
|
797
|
+
update: {
|
|
798
|
+
status: "active",
|
|
799
|
+
seats: 3,
|
|
800
|
+
},
|
|
801
|
+
where: [
|
|
802
|
+
{
|
|
803
|
+
field: "referenceId",
|
|
804
|
+
value: userRes.user.id,
|
|
805
|
+
},
|
|
806
|
+
],
|
|
807
|
+
});
|
|
808
|
+
|
|
809
|
+
const upgradeRes = await authClient.subscription.upgrade({
|
|
810
|
+
plan: "starter",
|
|
811
|
+
seats: 3,
|
|
812
|
+
fetchOptions: {
|
|
813
|
+
headers,
|
|
814
|
+
},
|
|
815
|
+
});
|
|
816
|
+
|
|
817
|
+
expect(upgradeRes.error).toBeDefined();
|
|
818
|
+
expect(upgradeRes.error?.message).toContain("already subscribed");
|
|
819
|
+
});
|
|
703
820
|
});
|