@better-auth/stripe 1.3.10-beta.1 → 1.3.10-beta.2
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 +3 -3
- package/dist/index.cjs +1 -2
- package/dist/index.mjs +1 -2
- package/package.json +3 -3
- package/src/index.ts +3 -2
- package/src/stripe.test.ts +149 -36
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
|
|
2
|
-
> @better-auth/stripe@1.3.10-beta.
|
|
2
|
+
> @better-auth/stripe@1.3.10-beta.2 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: 45.
|
|
8
|
+
[log] dist/index.cjs (total size: 45.4 kB, chunk size: 45.4 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: 44.
|
|
12
|
+
[log] dist/index.mjs (total size: 44.5 kB, chunk size: 44.5 kB, exports: stripe)
|
|
13
13
|
|
|
14
14
|
[log] dist/client.mjs (total size: 197 B, chunk size: 197 B, exports: stripeClient)
|
|
15
15
|
|
package/dist/index.cjs
CHANGED
|
@@ -660,9 +660,8 @@ const stripe = (options) => {
|
|
|
660
660
|
ctx
|
|
661
661
|
);
|
|
662
662
|
const hasEverTrialed = subscriptions.some((s) => {
|
|
663
|
-
const samePlan = s.plan?.toLowerCase() === plan.name.toLowerCase();
|
|
664
663
|
const hadTrial = !!(s.trialStart || s.trialEnd) || s.status === "trialing";
|
|
665
|
-
return
|
|
664
|
+
return hadTrial;
|
|
666
665
|
});
|
|
667
666
|
const freeTrial = !hasEverTrialed && plan.freeTrial ? { trial_period_days: plan.freeTrial.days } : void 0;
|
|
668
667
|
let priceIdToUse = void 0;
|
package/dist/index.mjs
CHANGED
|
@@ -644,9 +644,8 @@ const stripe = (options) => {
|
|
|
644
644
|
ctx
|
|
645
645
|
);
|
|
646
646
|
const hasEverTrialed = subscriptions.some((s) => {
|
|
647
|
-
const samePlan = s.plan?.toLowerCase() === plan.name.toLowerCase();
|
|
648
647
|
const hadTrial = !!(s.trialStart || s.trialEnd) || s.status === "trialing";
|
|
649
|
-
return
|
|
648
|
+
return hadTrial;
|
|
650
649
|
});
|
|
651
650
|
const freeTrial = !hasEverTrialed && plan.freeTrial ? { trial_period_days: plan.freeTrial.days } : void 0;
|
|
652
651
|
let priceIdToUse = void 0;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@better-auth/stripe",
|
|
3
3
|
"author": "Bereket Engida",
|
|
4
|
-
"version": "1.3.10-beta.
|
|
4
|
+
"version": "1.3.10-beta.2",
|
|
5
5
|
"main": "dist/index.cjs",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"keywords": [
|
|
@@ -41,13 +41,13 @@
|
|
|
41
41
|
},
|
|
42
42
|
"peerDependencies": {
|
|
43
43
|
"stripe": "^18",
|
|
44
|
-
"better-auth": "1.3.10-beta.
|
|
44
|
+
"better-auth": "1.3.10-beta.2"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
47
|
"better-call": "1.0.18",
|
|
48
48
|
"stripe": "^18.5.0",
|
|
49
49
|
"unbuild": "3.6.1",
|
|
50
|
-
"better-auth": "1.3.10-beta.
|
|
50
|
+
"better-auth": "1.3.10-beta.2"
|
|
51
51
|
},
|
|
52
52
|
"scripts": {
|
|
53
53
|
"test": "vitest",
|
package/src/index.ts
CHANGED
|
@@ -468,10 +468,11 @@ export const stripe = <O extends StripeOptions>(options: O) => {
|
|
|
468
468
|
);
|
|
469
469
|
|
|
470
470
|
const hasEverTrialed = subscriptions.some((s) => {
|
|
471
|
-
|
|
471
|
+
// Check if user has ever had a trial for any plan (not just the same plan)
|
|
472
|
+
// This prevents users from getting multiple trials by switching plans
|
|
472
473
|
const hadTrial =
|
|
473
474
|
!!(s.trialStart || s.trialEnd) || s.status === "trialing";
|
|
474
|
-
return
|
|
475
|
+
return hadTrial;
|
|
475
476
|
});
|
|
476
477
|
|
|
477
478
|
const freeTrial =
|
package/src/stripe.test.ts
CHANGED
|
@@ -1103,68 +1103,181 @@ describe("stripe", async () => {
|
|
|
1103
1103
|
expect(personalAfter?.status).toBe("active");
|
|
1104
1104
|
});
|
|
1105
1105
|
|
|
1106
|
-
it("should
|
|
1106
|
+
it("should prevent multiple free trials for the same user", async () => {
|
|
1107
1107
|
// Create a user
|
|
1108
1108
|
const userRes = await authClient.signUp.email(
|
|
1109
|
-
{
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1109
|
+
{ ...testUser, email: "trial-prevention@email.com" },
|
|
1110
|
+
{ throw: true },
|
|
1111
|
+
);
|
|
1112
|
+
|
|
1113
|
+
const headers = new Headers();
|
|
1114
|
+
await authClient.signIn.email(
|
|
1115
|
+
{ ...testUser, email: "trial-prevention@email.com" },
|
|
1114
1116
|
{
|
|
1115
1117
|
throw: true,
|
|
1118
|
+
onSuccess: setCookieToHeader(headers),
|
|
1116
1119
|
},
|
|
1117
1120
|
);
|
|
1118
1121
|
|
|
1122
|
+
// First subscription with trial
|
|
1123
|
+
const firstUpgradeRes = await authClient.subscription.upgrade({
|
|
1124
|
+
plan: "starter",
|
|
1125
|
+
fetchOptions: { headers },
|
|
1126
|
+
});
|
|
1127
|
+
|
|
1128
|
+
expect(firstUpgradeRes.data?.url).toBeDefined();
|
|
1129
|
+
|
|
1130
|
+
// Simulate the subscription being created with trial data
|
|
1131
|
+
await ctx.adapter.update({
|
|
1132
|
+
model: "subscription",
|
|
1133
|
+
update: {
|
|
1134
|
+
status: "trialing",
|
|
1135
|
+
trialStart: new Date(),
|
|
1136
|
+
trialEnd: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000), // 7 days from now
|
|
1137
|
+
},
|
|
1138
|
+
where: [
|
|
1139
|
+
{
|
|
1140
|
+
field: "referenceId",
|
|
1141
|
+
value: userRes.user.id,
|
|
1142
|
+
},
|
|
1143
|
+
],
|
|
1144
|
+
});
|
|
1145
|
+
|
|
1146
|
+
// Cancel the subscription
|
|
1147
|
+
await ctx.adapter.update({
|
|
1148
|
+
model: "subscription",
|
|
1149
|
+
update: {
|
|
1150
|
+
status: "canceled",
|
|
1151
|
+
},
|
|
1152
|
+
where: [
|
|
1153
|
+
{
|
|
1154
|
+
field: "referenceId",
|
|
1155
|
+
value: userRes.user.id,
|
|
1156
|
+
},
|
|
1157
|
+
],
|
|
1158
|
+
});
|
|
1159
|
+
|
|
1160
|
+
// Try to subscribe again - should NOT get a trial
|
|
1161
|
+
const secondUpgradeRes = await authClient.subscription.upgrade({
|
|
1162
|
+
plan: "starter",
|
|
1163
|
+
fetchOptions: { headers },
|
|
1164
|
+
});
|
|
1165
|
+
|
|
1166
|
+
expect(secondUpgradeRes.data?.url).toBeDefined();
|
|
1167
|
+
|
|
1168
|
+
// Verify that the checkout session was created without trial_period_days
|
|
1169
|
+
// We can't directly test the Stripe session, but we can verify the logic
|
|
1170
|
+
// by checking that the user has trial history
|
|
1171
|
+
const subscriptions = (await ctx.adapter.findMany({
|
|
1172
|
+
model: "subscription",
|
|
1173
|
+
where: [
|
|
1174
|
+
{
|
|
1175
|
+
field: "referenceId",
|
|
1176
|
+
value: userRes.user.id,
|
|
1177
|
+
},
|
|
1178
|
+
],
|
|
1179
|
+
})) as Subscription[];
|
|
1180
|
+
|
|
1181
|
+
// Should have 2 subscriptions (first canceled, second new)
|
|
1182
|
+
expect(subscriptions).toHaveLength(2);
|
|
1183
|
+
|
|
1184
|
+
// At least one should have trial data
|
|
1185
|
+
const hasTrialData = subscriptions.some(
|
|
1186
|
+
(s: Subscription) => s.trialStart || s.trialEnd,
|
|
1187
|
+
);
|
|
1188
|
+
expect(hasTrialData).toBe(true);
|
|
1189
|
+
});
|
|
1190
|
+
|
|
1191
|
+
it("should prevent multiple free trials across different plans", async () => {
|
|
1192
|
+
// Create a user
|
|
1193
|
+
const userRes = await authClient.signUp.email(
|
|
1194
|
+
{ ...testUser, email: "cross-plan-trial@email.com" },
|
|
1195
|
+
{ throw: true },
|
|
1196
|
+
);
|
|
1197
|
+
|
|
1119
1198
|
const headers = new Headers();
|
|
1120
1199
|
await authClient.signIn.email(
|
|
1121
|
-
{
|
|
1122
|
-
email: "incomplete@example.com",
|
|
1123
|
-
password: "password",
|
|
1124
|
-
},
|
|
1200
|
+
{ ...testUser, email: "cross-plan-trial@email.com" },
|
|
1125
1201
|
{
|
|
1126
1202
|
throw: true,
|
|
1127
1203
|
onSuccess: setCookieToHeader(headers),
|
|
1128
1204
|
},
|
|
1129
1205
|
);
|
|
1130
1206
|
|
|
1131
|
-
// First
|
|
1132
|
-
const
|
|
1207
|
+
// First subscription with trial on starter plan
|
|
1208
|
+
const firstUpgradeRes = await authClient.subscription.upgrade({
|
|
1133
1209
|
plan: "starter",
|
|
1134
|
-
fetchOptions: {
|
|
1135
|
-
|
|
1210
|
+
fetchOptions: { headers },
|
|
1211
|
+
});
|
|
1212
|
+
|
|
1213
|
+
expect(firstUpgradeRes.data?.url).toBeDefined();
|
|
1214
|
+
|
|
1215
|
+
// Simulate the subscription being created with trial data
|
|
1216
|
+
await ctx.adapter.update({
|
|
1217
|
+
model: "subscription",
|
|
1218
|
+
update: {
|
|
1219
|
+
status: "trialing",
|
|
1220
|
+
trialStart: new Date(),
|
|
1221
|
+
trialEnd: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000),
|
|
1136
1222
|
},
|
|
1223
|
+
where: [
|
|
1224
|
+
{
|
|
1225
|
+
field: "referenceId",
|
|
1226
|
+
value: userRes.user.id,
|
|
1227
|
+
},
|
|
1228
|
+
],
|
|
1137
1229
|
});
|
|
1138
|
-
expect(firstUpgrade.data?.url).toBe("https://checkout.stripe.com/mock");
|
|
1139
1230
|
|
|
1140
|
-
//
|
|
1141
|
-
|
|
1231
|
+
// Cancel the subscription
|
|
1232
|
+
await ctx.adapter.update({
|
|
1142
1233
|
model: "subscription",
|
|
1143
|
-
|
|
1234
|
+
update: {
|
|
1235
|
+
status: "canceled",
|
|
1236
|
+
},
|
|
1237
|
+
where: [
|
|
1238
|
+
{
|
|
1239
|
+
field: "referenceId",
|
|
1240
|
+
value: userRes.user.id,
|
|
1241
|
+
},
|
|
1242
|
+
],
|
|
1144
1243
|
});
|
|
1145
|
-
expect(subscriptions).toHaveLength(1);
|
|
1146
|
-
expect(subscriptions[0].status).toBe("incomplete");
|
|
1147
|
-
const firstSubId = subscriptions[0].id;
|
|
1148
1244
|
|
|
1149
|
-
//
|
|
1150
|
-
const
|
|
1245
|
+
// Try to subscribe to a different plan - should NOT get a trial
|
|
1246
|
+
const secondUpgradeRes = await authClient.subscription.upgrade({
|
|
1151
1247
|
plan: "premium",
|
|
1152
|
-
|
|
1153
|
-
fetchOptions: {
|
|
1154
|
-
headers,
|
|
1155
|
-
},
|
|
1248
|
+
fetchOptions: { headers },
|
|
1156
1249
|
});
|
|
1157
|
-
expect(secondUpgrade.data?.url).toBe("https://checkout.stripe.com/mock");
|
|
1158
1250
|
|
|
1159
|
-
|
|
1160
|
-
|
|
1251
|
+
expect(secondUpgradeRes.data?.url).toBeDefined();
|
|
1252
|
+
|
|
1253
|
+
// Verify that the user has trial history from the first plan
|
|
1254
|
+
const subscriptions = (await ctx.adapter.findMany({
|
|
1161
1255
|
model: "subscription",
|
|
1162
|
-
where: [
|
|
1256
|
+
where: [
|
|
1257
|
+
{
|
|
1258
|
+
field: "referenceId",
|
|
1259
|
+
value: userRes.user.id,
|
|
1260
|
+
},
|
|
1261
|
+
],
|
|
1262
|
+
})) as Subscription[];
|
|
1263
|
+
|
|
1264
|
+
// Should have at least 1 subscription (the starter with trial data)
|
|
1265
|
+
expect(subscriptions.length).toBeGreaterThanOrEqual(1);
|
|
1266
|
+
|
|
1267
|
+
// The starter subscription should have trial data
|
|
1268
|
+
const starterSub = subscriptions.find(
|
|
1269
|
+
(s: Subscription) => s.plan === "starter",
|
|
1270
|
+
) as Subscription | undefined;
|
|
1271
|
+
expect(starterSub?.trialStart).toBeDefined();
|
|
1272
|
+
expect(starterSub?.trialEnd).toBeDefined();
|
|
1273
|
+
|
|
1274
|
+
// Verify that the trial eligibility logic is working by checking
|
|
1275
|
+
// that the user has ever had a trial (which should prevent future trials)
|
|
1276
|
+
const hasEverTrialed = subscriptions.some((s: Subscription) => {
|
|
1277
|
+
const hadTrial =
|
|
1278
|
+
!!(s.trialStart || s.trialEnd) || s.status === "trialing";
|
|
1279
|
+
return hadTrial;
|
|
1163
1280
|
});
|
|
1164
|
-
expect(
|
|
1165
|
-
expect(subscriptionsAfter[0].id).toBe(firstSubId);
|
|
1166
|
-
expect(subscriptionsAfter[0].status).toBe("incomplete");
|
|
1167
|
-
expect(subscriptionsAfter[0].plan).toBe("premium");
|
|
1168
|
-
expect(subscriptionsAfter[0].seats).toBe(2);
|
|
1281
|
+
expect(hasEverTrialed).toBe(true);
|
|
1169
1282
|
});
|
|
1170
1283
|
});
|