@better-auth/stripe 1.4.7-beta.2 → 1.4.7-beta.4
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 +6 -6
- package/dist/client.d.mts +1 -1
- package/dist/{index-B_7z2Xag.d.mts → index-Dbyi2yVo.d.mts} +1 -17
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +8 -5
- package/package.json +5 -5
- package/src/routes.ts +31 -4
- package/src/stripe.test.ts +86 -0
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
> @better-auth/stripe@1.4.7-beta.
|
|
2
|
+
> @better-auth/stripe@1.4.7-beta.4 build /home/runner/work/better-auth/better-auth/packages/stripe
|
|
3
3
|
> tsdown
|
|
4
4
|
|
|
5
5
|
[34mℹ[39m tsdown [2mv0.17.2[22m powered by rolldown [2mv1.0.0-beta.53[22m
|
|
@@ -7,10 +7,10 @@
|
|
|
7
7
|
[34mℹ[39m entry: [34msrc/index.ts, src/client.ts[39m
|
|
8
8
|
[34mℹ[39m tsconfig: [34mtsconfig.json[39m
|
|
9
9
|
[34mℹ[39m Build start
|
|
10
|
-
[34mℹ[39m [2mdist/[22m[1mindex.mjs[22m [2m40.
|
|
10
|
+
[34mℹ[39m [2mdist/[22m[1mindex.mjs[22m [2m40.71 kB[22m [2m│ gzip: 7.59 kB[22m
|
|
11
11
|
[34mℹ[39m [2mdist/[22m[1mclient.mjs[22m [2m 0.26 kB[22m [2m│ gzip: 0.20 kB[22m
|
|
12
|
-
[34mℹ[39m [2mdist/[22m[32m[1mclient.d.mts[22m[39m [2m 0.62 kB[22m [2m│ gzip: 0.
|
|
12
|
+
[34mℹ[39m [2mdist/[22m[32m[1mclient.d.mts[22m[39m [2m 0.62 kB[22m [2m│ gzip: 0.34 kB[22m
|
|
13
13
|
[34mℹ[39m [2mdist/[22m[32m[1mindex.d.mts[22m[39m [2m 0.21 kB[22m [2m│ gzip: 0.14 kB[22m
|
|
14
|
-
[34mℹ[39m [2mdist/[22m[32mindex-
|
|
15
|
-
[34mℹ[39m 5 files, total: 66.
|
|
16
|
-
[32m✔[39m Build complete in [
|
|
14
|
+
[34mℹ[39m [2mdist/[22m[32mindex-Dbyi2yVo.d.mts[39m [2m24.87 kB[22m [2m│ gzip: 4.82 kB[22m
|
|
15
|
+
[34mℹ[39m 5 files, total: 66.68 kB
|
|
16
|
+
[32m✔[39m Build complete in [32m12247ms[39m
|
package/dist/client.d.mts
CHANGED
|
@@ -366,15 +366,13 @@ declare const stripe: <O extends StripeOptions>(options: O) => {
|
|
|
366
366
|
stripeWebhook: better_call0.StrictEndpoint<"/stripe/webhook", {
|
|
367
367
|
method: "POST";
|
|
368
368
|
metadata: {
|
|
369
|
-
isAction: false;
|
|
370
369
|
openapi: {
|
|
371
370
|
operationId: string;
|
|
372
371
|
};
|
|
372
|
+
scope: "server";
|
|
373
373
|
};
|
|
374
374
|
cloneRequest: true;
|
|
375
375
|
disableBody: true;
|
|
376
|
-
} & {
|
|
377
|
-
use: any[];
|
|
378
376
|
}, {
|
|
379
377
|
success: boolean;
|
|
380
378
|
}>;
|
|
@@ -423,8 +421,6 @@ declare const stripe: <O extends StripeOptions>(options: O) => {
|
|
|
423
421
|
};
|
|
424
422
|
};
|
|
425
423
|
}>))[];
|
|
426
|
-
} & {
|
|
427
|
-
use: any[];
|
|
428
424
|
}, {
|
|
429
425
|
url: string;
|
|
430
426
|
redirect: boolean;
|
|
@@ -515,8 +511,6 @@ declare const stripe: <O extends StripeOptions>(options: O) => {
|
|
|
515
511
|
};
|
|
516
512
|
};
|
|
517
513
|
use: ((inputContext: better_call0.MiddlewareInputContext<better_call0.MiddlewareOptions>) => Promise<void>)[];
|
|
518
|
-
} & {
|
|
519
|
-
use: any[];
|
|
520
514
|
}, never>;
|
|
521
515
|
cancelSubscription: better_call0.StrictEndpoint<"/subscription/cancel", {
|
|
522
516
|
method: "POST";
|
|
@@ -553,8 +547,6 @@ declare const stripe: <O extends StripeOptions>(options: O) => {
|
|
|
553
547
|
};
|
|
554
548
|
};
|
|
555
549
|
}>))[];
|
|
556
|
-
} & {
|
|
557
|
-
use: any[];
|
|
558
550
|
}, {
|
|
559
551
|
url: string;
|
|
560
552
|
redirect: boolean;
|
|
@@ -593,8 +585,6 @@ declare const stripe: <O extends StripeOptions>(options: O) => {
|
|
|
593
585
|
};
|
|
594
586
|
};
|
|
595
587
|
}>))[];
|
|
596
|
-
} & {
|
|
597
|
-
use: any[];
|
|
598
588
|
}, Stripe.Response<Stripe.Subscription>>;
|
|
599
589
|
listActiveSubscriptions: better_call0.StrictEndpoint<"/subscription/list", {
|
|
600
590
|
method: "GET";
|
|
@@ -629,8 +619,6 @@ declare const stripe: <O extends StripeOptions>(options: O) => {
|
|
|
629
619
|
};
|
|
630
620
|
};
|
|
631
621
|
}>))[];
|
|
632
|
-
} & {
|
|
633
|
-
use: any[];
|
|
634
622
|
}, {
|
|
635
623
|
limits: Record<string, unknown> | undefined;
|
|
636
624
|
priceId: string | undefined;
|
|
@@ -657,8 +645,6 @@ declare const stripe: <O extends StripeOptions>(options: O) => {
|
|
|
657
645
|
};
|
|
658
646
|
};
|
|
659
647
|
use: ((inputContext: better_call0.MiddlewareInputContext<better_call0.MiddlewareOptions>) => Promise<void>)[];
|
|
660
|
-
} & {
|
|
661
|
-
use: any[];
|
|
662
648
|
}, {
|
|
663
649
|
status: ("OK" | "CREATED" | "ACCEPTED" | "NO_CONTENT" | "MULTIPLE_CHOICES" | "MOVED_PERMANENTLY" | "FOUND" | "SEE_OTHER" | "NOT_MODIFIED" | "TEMPORARY_REDIRECT" | "BAD_REQUEST" | "UNAUTHORIZED" | "PAYMENT_REQUIRED" | "FORBIDDEN" | "NOT_FOUND" | "METHOD_NOT_ALLOWED" | "NOT_ACCEPTABLE" | "PROXY_AUTHENTICATION_REQUIRED" | "REQUEST_TIMEOUT" | "CONFLICT" | "GONE" | "LENGTH_REQUIRED" | "PRECONDITION_FAILED" | "PAYLOAD_TOO_LARGE" | "URI_TOO_LONG" | "UNSUPPORTED_MEDIA_TYPE" | "RANGE_NOT_SATISFIABLE" | "EXPECTATION_FAILED" | "I'M_A_TEAPOT" | "MISDIRECTED_REQUEST" | "UNPROCESSABLE_ENTITY" | "LOCKED" | "FAILED_DEPENDENCY" | "TOO_EARLY" | "UPGRADE_REQUIRED" | "PRECONDITION_REQUIRED" | "TOO_MANY_REQUESTS" | "REQUEST_HEADER_FIELDS_TOO_LARGE" | "UNAVAILABLE_FOR_LEGAL_REASONS" | "INTERNAL_SERVER_ERROR" | "NOT_IMPLEMENTED" | "BAD_GATEWAY" | "SERVICE_UNAVAILABLE" | "GATEWAY_TIMEOUT" | "HTTP_VERSION_NOT_SUPPORTED" | "VARIANT_ALSO_NEGOTIATES" | "INSUFFICIENT_STORAGE" | "LOOP_DETECTED" | "NOT_EXTENDED" | "NETWORK_AUTHENTICATION_REQUIRED") | better_call0.Status;
|
|
664
650
|
body: ({
|
|
@@ -708,8 +694,6 @@ declare const stripe: <O extends StripeOptions>(options: O) => {
|
|
|
708
694
|
};
|
|
709
695
|
};
|
|
710
696
|
}>))[];
|
|
711
|
-
} & {
|
|
712
|
-
use: any[];
|
|
713
697
|
}, {
|
|
714
698
|
url: string;
|
|
715
699
|
redirect: boolean;
|
package/dist/index.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as SubscriptionOptions, i as Subscription, n as stripe, r as StripePlan, t as StripePlugin } from "./index-
|
|
1
|
+
import { a as SubscriptionOptions, i as Subscription, n as stripe, r as StripePlan, t as StripePlugin } from "./index-Dbyi2yVo.mjs";
|
|
2
2
|
export { StripePlan, StripePlugin, Subscription, SubscriptionOptions, stripe };
|
package/dist/index.mjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { defineErrorCodes } from "@better-auth/core/utils";
|
|
2
2
|
import { defu } from "defu";
|
|
3
3
|
import { createAuthEndpoint, createAuthMiddleware } from "@better-auth/core/api";
|
|
4
|
+
import { HIDE_METADATA, logger } from "better-auth";
|
|
4
5
|
import { APIError, getSessionFromCtx, originCheck, sessionMiddleware } from "better-auth/api";
|
|
5
6
|
import * as z from "zod/v4";
|
|
6
|
-
import { logger } from "better-auth";
|
|
7
7
|
import { mergeSchema } from "better-auth/db";
|
|
8
8
|
|
|
9
9
|
//#region src/utils.ts
|
|
@@ -265,7 +265,7 @@ const upgradeSubscription = (options) => {
|
|
|
265
265
|
const referenceId = ctx.body.referenceId || user$1.id;
|
|
266
266
|
const plan = await getPlanByName(options, ctx.body.plan);
|
|
267
267
|
if (!plan) throw new APIError("BAD_REQUEST", { message: STRIPE_ERROR_CODES$1.SUBSCRIPTION_PLAN_NOT_FOUND });
|
|
268
|
-
|
|
268
|
+
let subscriptionToUpdate = ctx.body.subscriptionId ? await ctx.context.adapter.findOne({
|
|
269
269
|
model: "subscription",
|
|
270
270
|
where: [{
|
|
271
271
|
field: "id",
|
|
@@ -283,6 +283,7 @@ const upgradeSubscription = (options) => {
|
|
|
283
283
|
value: referenceId
|
|
284
284
|
}]
|
|
285
285
|
}) : null;
|
|
286
|
+
if (ctx.body.subscriptionId && subscriptionToUpdate && subscriptionToUpdate.referenceId !== referenceId) subscriptionToUpdate = null;
|
|
286
287
|
if (ctx.body.subscriptionId && !subscriptionToUpdate) throw new APIError("BAD_REQUEST", { message: STRIPE_ERROR_CODES$1.SUBSCRIPTION_NOT_FOUND });
|
|
287
288
|
let customerId = subscriptionToUpdate?.stripeCustomerId || user$1.stripeCustomerId;
|
|
288
289
|
if (!customerId) try {
|
|
@@ -553,7 +554,7 @@ const cancelSubscription = (options) => {
|
|
|
553
554
|
]
|
|
554
555
|
}, async (ctx) => {
|
|
555
556
|
const referenceId = ctx.body?.referenceId || ctx.context.session.user.id;
|
|
556
|
-
|
|
557
|
+
let subscription = ctx.body.subscriptionId ? await ctx.context.adapter.findOne({
|
|
557
558
|
model: "subscription",
|
|
558
559
|
where: [{
|
|
559
560
|
field: "id",
|
|
@@ -566,6 +567,7 @@ const cancelSubscription = (options) => {
|
|
|
566
567
|
value: referenceId
|
|
567
568
|
}]
|
|
568
569
|
}).then((subs) => subs.find((sub) => sub.status === "active" || sub.status === "trialing"));
|
|
570
|
+
if (ctx.body.subscriptionId && subscription && subscription.referenceId !== referenceId) subscription = void 0;
|
|
569
571
|
if (!subscription || !subscription.stripeCustomerId) throw ctx.error("BAD_REQUEST", { message: STRIPE_ERROR_CODES$1.SUBSCRIPTION_NOT_FOUND });
|
|
570
572
|
const activeSubscriptions = await client.subscriptions.list({ customer: subscription.stripeCustomerId }).then((res) => res.data.filter((sub) => sub.status === "active" || sub.status === "trialing"));
|
|
571
573
|
if (!activeSubscriptions.length) {
|
|
@@ -631,7 +633,7 @@ const restoreSubscription = (options) => {
|
|
|
631
633
|
use: [sessionMiddleware, referenceMiddleware(subscriptionOptions, "restore-subscription")]
|
|
632
634
|
}, async (ctx) => {
|
|
633
635
|
const referenceId = ctx.body?.referenceId || ctx.context.session.user.id;
|
|
634
|
-
|
|
636
|
+
let subscription = ctx.body.subscriptionId ? await ctx.context.adapter.findOne({
|
|
635
637
|
model: "subscription",
|
|
636
638
|
where: [{
|
|
637
639
|
field: "id",
|
|
@@ -644,6 +646,7 @@ const restoreSubscription = (options) => {
|
|
|
644
646
|
value: referenceId
|
|
645
647
|
}]
|
|
646
648
|
}).then((subs) => subs.find((sub) => sub.status === "active" || sub.status === "trialing"));
|
|
649
|
+
if (ctx.body.subscriptionId && subscription && subscription.referenceId !== referenceId) subscription = void 0;
|
|
647
650
|
if (!subscription || !subscription.stripeCustomerId) throw ctx.error("BAD_REQUEST", { message: STRIPE_ERROR_CODES$1.SUBSCRIPTION_NOT_FOUND });
|
|
648
651
|
if (subscription.status != "active" && subscription.status != "trialing") throw ctx.error("BAD_REQUEST", { message: STRIPE_ERROR_CODES$1.SUBSCRIPTION_NOT_ACTIVE });
|
|
649
652
|
if (!subscription.cancelAtPeriodEnd) throw ctx.error("BAD_REQUEST", { message: STRIPE_ERROR_CODES$1.SUBSCRIPTION_NOT_SCHEDULED_FOR_CANCELLATION });
|
|
@@ -824,7 +827,7 @@ const stripeWebhook = (options) => {
|
|
|
824
827
|
return createAuthEndpoint("/stripe/webhook", {
|
|
825
828
|
method: "POST",
|
|
826
829
|
metadata: {
|
|
827
|
-
|
|
830
|
+
...HIDE_METADATA,
|
|
828
831
|
openapi: { operationId: "handleStripeWebhook" }
|
|
829
832
|
},
|
|
830
833
|
cloneRequest: true,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@better-auth/stripe",
|
|
3
3
|
"author": "Bereket Engida",
|
|
4
|
-
"version": "1.4.7-beta.
|
|
4
|
+
"version": "1.4.7-beta.4",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.mjs",
|
|
7
7
|
"types": "dist/index.d.mts",
|
|
@@ -50,15 +50,15 @@
|
|
|
50
50
|
},
|
|
51
51
|
"peerDependencies": {
|
|
52
52
|
"stripe": "^18 || ^19 || ^20",
|
|
53
|
-
"@better-auth/core": "1.4.7-beta.
|
|
54
|
-
"better-auth": "1.4.7-beta.
|
|
53
|
+
"@better-auth/core": "1.4.7-beta.4",
|
|
54
|
+
"better-auth": "1.4.7-beta.4"
|
|
55
55
|
},
|
|
56
56
|
"devDependencies": {
|
|
57
57
|
"better-call": "1.1.5",
|
|
58
58
|
"stripe": "^20.0.0",
|
|
59
59
|
"tsdown": "^0.17.2",
|
|
60
|
-
"@better-auth/core": "1.4.7-beta.
|
|
61
|
-
"better-auth": "1.4.7-beta.
|
|
60
|
+
"@better-auth/core": "1.4.7-beta.4",
|
|
61
|
+
"better-auth": "1.4.7-beta.4"
|
|
62
62
|
},
|
|
63
63
|
"scripts": {
|
|
64
64
|
"test": "vitest",
|
package/src/routes.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createAuthEndpoint } from "@better-auth/core/api";
|
|
2
2
|
import { defineErrorCodes } from "@better-auth/core/utils";
|
|
3
3
|
import type { GenericEndpointContext } from "better-auth";
|
|
4
|
+
import { HIDE_METADATA } from "better-auth";
|
|
4
5
|
import {
|
|
5
6
|
APIError,
|
|
6
7
|
getSessionFromCtx,
|
|
@@ -181,7 +182,7 @@ export const upgradeSubscription = (options: StripeOptions) => {
|
|
|
181
182
|
message: STRIPE_ERROR_CODES.SUBSCRIPTION_PLAN_NOT_FOUND,
|
|
182
183
|
});
|
|
183
184
|
}
|
|
184
|
-
|
|
185
|
+
let subscriptionToUpdate = ctx.body.subscriptionId
|
|
185
186
|
? await ctx.context.adapter.findOne<Subscription>({
|
|
186
187
|
model: "subscription",
|
|
187
188
|
where: [
|
|
@@ -204,6 +205,14 @@ export const upgradeSubscription = (options: StripeOptions) => {
|
|
|
204
205
|
})
|
|
205
206
|
: null;
|
|
206
207
|
|
|
208
|
+
if (
|
|
209
|
+
ctx.body.subscriptionId &&
|
|
210
|
+
subscriptionToUpdate &&
|
|
211
|
+
subscriptionToUpdate.referenceId !== referenceId
|
|
212
|
+
) {
|
|
213
|
+
subscriptionToUpdate = null;
|
|
214
|
+
}
|
|
215
|
+
|
|
207
216
|
if (ctx.body.subscriptionId && !subscriptionToUpdate) {
|
|
208
217
|
throw new APIError("BAD_REQUEST", {
|
|
209
218
|
message: STRIPE_ERROR_CODES.SUBSCRIPTION_NOT_FOUND,
|
|
@@ -697,7 +706,7 @@ export const cancelSubscription = (options: StripeOptions) => {
|
|
|
697
706
|
},
|
|
698
707
|
async (ctx) => {
|
|
699
708
|
const referenceId = ctx.body?.referenceId || ctx.context.session.user.id;
|
|
700
|
-
|
|
709
|
+
let subscription = ctx.body.subscriptionId
|
|
701
710
|
? await ctx.context.adapter.findOne<Subscription>({
|
|
702
711
|
model: "subscription",
|
|
703
712
|
where: [
|
|
@@ -718,6 +727,15 @@ export const cancelSubscription = (options: StripeOptions) => {
|
|
|
718
727
|
),
|
|
719
728
|
);
|
|
720
729
|
|
|
730
|
+
// Ensure the specified subscription belongs to the (validated) referenceId.
|
|
731
|
+
if (
|
|
732
|
+
ctx.body.subscriptionId &&
|
|
733
|
+
subscription &&
|
|
734
|
+
subscription.referenceId !== referenceId
|
|
735
|
+
) {
|
|
736
|
+
subscription = undefined;
|
|
737
|
+
}
|
|
738
|
+
|
|
721
739
|
if (!subscription || !subscription.stripeCustomerId) {
|
|
722
740
|
throw ctx.error("BAD_REQUEST", {
|
|
723
741
|
message: STRIPE_ERROR_CODES.SUBSCRIPTION_NOT_FOUND,
|
|
@@ -846,7 +864,7 @@ export const restoreSubscription = (options: StripeOptions) => {
|
|
|
846
864
|
async (ctx) => {
|
|
847
865
|
const referenceId = ctx.body?.referenceId || ctx.context.session.user.id;
|
|
848
866
|
|
|
849
|
-
|
|
867
|
+
let subscription = ctx.body.subscriptionId
|
|
850
868
|
? await ctx.context.adapter.findOne<Subscription>({
|
|
851
869
|
model: "subscription",
|
|
852
870
|
where: [
|
|
@@ -871,6 +889,15 @@ export const restoreSubscription = (options: StripeOptions) => {
|
|
|
871
889
|
(sub) => sub.status === "active" || sub.status === "trialing",
|
|
872
890
|
),
|
|
873
891
|
);
|
|
892
|
+
|
|
893
|
+
// Ensure the specified subscription belongs to the (validated) referenceId.
|
|
894
|
+
if (
|
|
895
|
+
ctx.body.subscriptionId &&
|
|
896
|
+
subscription &&
|
|
897
|
+
subscription.referenceId !== referenceId
|
|
898
|
+
) {
|
|
899
|
+
subscription = undefined;
|
|
900
|
+
}
|
|
874
901
|
if (!subscription || !subscription.stripeCustomerId) {
|
|
875
902
|
throw ctx.error("BAD_REQUEST", {
|
|
876
903
|
message: STRIPE_ERROR_CODES.SUBSCRIPTION_NOT_FOUND,
|
|
@@ -1221,7 +1248,7 @@ export const stripeWebhook = (options: StripeOptions) => {
|
|
|
1221
1248
|
{
|
|
1222
1249
|
method: "POST",
|
|
1223
1250
|
metadata: {
|
|
1224
|
-
|
|
1251
|
+
...HIDE_METADATA,
|
|
1225
1252
|
openapi: {
|
|
1226
1253
|
operationId: "handleStripeWebhook",
|
|
1227
1254
|
},
|
package/src/stripe.test.ts
CHANGED
|
@@ -227,6 +227,92 @@ describe("stripe", async () => {
|
|
|
227
227
|
});
|
|
228
228
|
});
|
|
229
229
|
|
|
230
|
+
it("should not allow cross-user subscriptionId operations (upgrade/cancel/restore)", async () => {
|
|
231
|
+
const userA = {
|
|
232
|
+
email: "user-a@email.com",
|
|
233
|
+
password: "password",
|
|
234
|
+
name: "User A",
|
|
235
|
+
};
|
|
236
|
+
const userARes = await authClient.signUp.email(userA, { throw: true });
|
|
237
|
+
|
|
238
|
+
const userAHeaders = new Headers();
|
|
239
|
+
await authClient.signIn.email(userA, {
|
|
240
|
+
throw: true,
|
|
241
|
+
onSuccess: setCookieToHeader(userAHeaders),
|
|
242
|
+
});
|
|
243
|
+
await authClient.subscription.upgrade({
|
|
244
|
+
plan: "starter",
|
|
245
|
+
fetchOptions: { headers: userAHeaders },
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
const userASub = await ctx.adapter.findOne<Subscription>({
|
|
249
|
+
model: "subscription",
|
|
250
|
+
where: [{ field: "referenceId", value: userARes.user.id }],
|
|
251
|
+
});
|
|
252
|
+
expect(userASub).toBeTruthy();
|
|
253
|
+
|
|
254
|
+
const userB = {
|
|
255
|
+
email: "user-b@email.com",
|
|
256
|
+
password: "password",
|
|
257
|
+
name: "User B",
|
|
258
|
+
};
|
|
259
|
+
await authClient.signUp.email(userB, { throw: true });
|
|
260
|
+
const userBHeaders = new Headers();
|
|
261
|
+
await authClient.signIn.email(userB, {
|
|
262
|
+
throw: true,
|
|
263
|
+
onSuccess: setCookieToHeader(userBHeaders),
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
mockStripe.checkout.sessions.create.mockClear();
|
|
267
|
+
mockStripe.billingPortal.sessions.create.mockClear();
|
|
268
|
+
mockStripe.subscriptions.list.mockClear();
|
|
269
|
+
mockStripe.subscriptions.update.mockClear();
|
|
270
|
+
|
|
271
|
+
const upgradeRes = await authClient.subscription.upgrade({
|
|
272
|
+
plan: "premium",
|
|
273
|
+
subscriptionId: userASub!.id,
|
|
274
|
+
fetchOptions: { headers: userBHeaders },
|
|
275
|
+
});
|
|
276
|
+
expect(upgradeRes.error?.message).toContain("Subscription not found");
|
|
277
|
+
expect(mockStripe.checkout.sessions.create).not.toHaveBeenCalled();
|
|
278
|
+
expect(mockStripe.billingPortal.sessions.create).not.toHaveBeenCalled();
|
|
279
|
+
|
|
280
|
+
const cancelHeaders = new Headers(userBHeaders);
|
|
281
|
+
cancelHeaders.set("content-type", "application/json");
|
|
282
|
+
const cancelResponse = await auth.handler(
|
|
283
|
+
new Request("http://localhost:3000/api/auth/subscription/cancel", {
|
|
284
|
+
method: "POST",
|
|
285
|
+
headers: cancelHeaders,
|
|
286
|
+
body: JSON.stringify({
|
|
287
|
+
subscriptionId: userASub!.id,
|
|
288
|
+
returnUrl: "/account",
|
|
289
|
+
}),
|
|
290
|
+
}),
|
|
291
|
+
);
|
|
292
|
+
expect(cancelResponse.status).toBe(400);
|
|
293
|
+
expect((await cancelResponse.json()).message).toContain(
|
|
294
|
+
"Subscription not found",
|
|
295
|
+
);
|
|
296
|
+
expect(mockStripe.billingPortal.sessions.create).not.toHaveBeenCalled();
|
|
297
|
+
|
|
298
|
+
const restoreHeaders = new Headers(userBHeaders);
|
|
299
|
+
restoreHeaders.set("content-type", "application/json");
|
|
300
|
+
const restoreResponse = await auth.handler(
|
|
301
|
+
new Request("http://localhost:3000/api/auth/subscription/restore", {
|
|
302
|
+
method: "POST",
|
|
303
|
+
headers: restoreHeaders,
|
|
304
|
+
body: JSON.stringify({
|
|
305
|
+
subscriptionId: userASub!.id,
|
|
306
|
+
}),
|
|
307
|
+
}),
|
|
308
|
+
);
|
|
309
|
+
expect(restoreResponse.status).toBe(400);
|
|
310
|
+
expect((await restoreResponse.json()).message).toContain(
|
|
311
|
+
"Subscription not found",
|
|
312
|
+
);
|
|
313
|
+
expect(mockStripe.subscriptions.update).not.toHaveBeenCalled();
|
|
314
|
+
});
|
|
315
|
+
|
|
230
316
|
it("should list active subscriptions", async () => {
|
|
231
317
|
const userRes = await authClient.signUp.email(
|
|
232
318
|
{
|