@better-auth/stripe 1.3.0-beta.8 → 1.3.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.
@@ -1,17 +1,17 @@
1
1
 
2
- > @better-auth/stripe@1.3.0-beta.8 build /home/runner/work/better-auth/better-auth/packages/stripe
2
+ > @better-auth/stripe@1.3.0 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: 38.5 kB, chunk size: 38.5 kB, exports: stripe)
8
+ [log] dist/index.cjs (total size: 42.3 kB, chunk size: 42.3 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: 38.2 kB, chunk size: 38.2 kB, exports: stripe)
12
+ [log] dist/index.mjs (total size: 41.6 kB, chunk size: 41.6 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): 204 kB
16
+ Σ Total dist size (byte size): 206 kB
17
17
  [log]
package/dist/client.d.cts CHANGED
@@ -2,7 +2,7 @@ import { stripe } from './index.cjs';
2
2
  import 'better-auth';
3
3
  import 'better-call';
4
4
  import 'stripe';
5
- import 'zod';
5
+ import 'zod/v4';
6
6
  import 'better-auth/api';
7
7
 
8
8
  declare const stripeClient: <O extends {
package/dist/client.d.mts CHANGED
@@ -2,7 +2,7 @@ import { stripe } from './index.mjs';
2
2
  import 'better-auth';
3
3
  import 'better-call';
4
4
  import 'stripe';
5
- import 'zod';
5
+ import 'zod/v4';
6
6
  import 'better-auth/api';
7
7
 
8
8
  declare const stripeClient: <O extends {
package/dist/client.d.ts CHANGED
@@ -2,7 +2,7 @@ import { stripe } from './index.js';
2
2
  import 'better-auth';
3
3
  import 'better-call';
4
4
  import 'stripe';
5
- import 'zod';
5
+ import 'zod/v4';
6
6
  import 'better-auth/api';
7
7
 
8
8
  declare const stripeClient: <O extends {
package/dist/index.cjs CHANGED
@@ -2,11 +2,24 @@
2
2
 
3
3
  const betterAuth = require('better-auth');
4
4
  const plugins = require('better-auth/plugins');
5
- const zod = require('zod');
5
+ const z = require('zod/v4');
6
6
  const api = require('better-auth/api');
7
- const crypto = require('better-auth/crypto');
8
7
  const db = require('better-auth/db');
9
8
 
9
+ function _interopNamespaceCompat(e) {
10
+ if (e && typeof e === 'object' && 'default' in e) return e;
11
+ const n = Object.create(null);
12
+ if (e) {
13
+ for (const k in e) {
14
+ n[k] = e[k];
15
+ }
16
+ }
17
+ n.default = e;
18
+ return n;
19
+ }
20
+
21
+ const z__namespace = /*#__PURE__*/_interopNamespaceCompat(z);
22
+
10
23
  async function getPlans(options) {
11
24
  return typeof options?.subscription?.plans === "function" ? await options.subscription?.plans() : options.subscription?.plans;
12
25
  }
@@ -81,12 +94,15 @@ async function onCheckoutSessionCompleted(ctx, options, event) {
81
94
  ]
82
95
  });
83
96
  }
84
- await options.subscription?.onSubscriptionComplete?.({
85
- event,
86
- subscription: dbSubscription,
87
- stripeSubscription: subscription,
88
- plan
89
- });
97
+ await options.subscription?.onSubscriptionComplete?.(
98
+ {
99
+ event,
100
+ subscription: dbSubscription,
101
+ stripeSubscription: subscription,
102
+ plan
103
+ },
104
+ ctx
105
+ );
90
106
  return;
91
107
  }
92
108
  }
@@ -170,10 +186,10 @@ async function onSubscriptionUpdated(ctx, options, event) {
170
186
  });
171
187
  if (plan) {
172
188
  if (subscriptionUpdated.status === "active" && subscription.status === "trialing" && plan.freeTrial?.onTrialEnd) {
173
- await plan.freeTrial.onTrialEnd({ subscription }, ctx.request);
189
+ await plan.freeTrial.onTrialEnd({ subscription }, ctx);
174
190
  }
175
191
  if (subscriptionUpdated.status === "incomplete_expired" && subscription.status === "trialing" && plan.freeTrial?.onTrialExpired) {
176
- await plan.freeTrial.onTrialExpired(subscription, ctx.request);
192
+ await plan.freeTrial.onTrialExpired(subscription, ctx);
177
193
  }
178
194
  }
179
195
  } catch (error) {
@@ -331,12 +347,15 @@ const stripe = (options) => {
331
347
  message: "Reference id is not allowed. Read server logs for more details."
332
348
  });
333
349
  }
334
- const isAuthorized = ctx.body?.referenceId ? await options.subscription?.authorizeReference?.({
335
- user: session.user,
336
- session: session.session,
337
- referenceId,
338
- action
339
- }) : true;
350
+ const isAuthorized = ctx.body?.referenceId ? await options.subscription?.authorizeReference?.(
351
+ {
352
+ user: session.user,
353
+ session: session.session,
354
+ referenceId,
355
+ action
356
+ },
357
+ ctx
358
+ ) : true;
340
359
  if (!isAuthorized) {
341
360
  throw new api.APIError("UNAUTHORIZED", {
342
361
  message: "Unauthorized"
@@ -344,70 +363,89 @@ const stripe = (options) => {
344
363
  }
345
364
  });
346
365
  const subscriptionEndpoints = {
366
+ /**
367
+ * ### Endpoint
368
+ *
369
+ * POST `/subscription/upgrade`
370
+ *
371
+ * ### API Methods
372
+ *
373
+ * **server:**
374
+ * `auth.api.upgradeSubscription`
375
+ *
376
+ * **client:**
377
+ * `authClient.subscription.upgrade`
378
+ *
379
+ * @see [Read our docs to learn more.](https://better-auth.com/docs/plugins/stripe#api-method-subscription-upgrade)
380
+ */
347
381
  upgradeSubscription: plugins.createAuthEndpoint(
348
382
  "/subscription/upgrade",
349
383
  {
350
384
  method: "POST",
351
- body: zod.z.object({
385
+ body: z__namespace.object({
352
386
  /**
353
387
  * The name of the plan to subscribe
354
388
  */
355
- plan: zod.z.string({
356
- description: "The name of the plan to upgrade to"
389
+ plan: z__namespace.string().meta({
390
+ description: 'The name of the plan to upgrade to. Eg: "pro"'
357
391
  }),
358
392
  /**
359
393
  * If annual plan should be applied.
360
394
  */
361
- annual: zod.z.boolean({
362
- description: "Whether to upgrade to an annual plan"
395
+ annual: z__namespace.boolean().meta({
396
+ description: "Whether to upgrade to an annual plan. Eg: true"
363
397
  }).optional(),
364
398
  /**
365
399
  * Reference id of the subscription to upgrade
366
400
  * This is used to identify the subscription to upgrade
367
401
  * If not provided, the user's id will be used
368
402
  */
369
- referenceId: zod.z.string({
370
- description: "Reference id of the subscription to upgrade"
403
+ referenceId: z__namespace.string().meta({
404
+ description: 'Reference id of the subscription to upgrade. Eg: "123"'
371
405
  }).optional(),
372
406
  /**
373
407
  * This is to allow a specific subscription to be upgrade.
374
408
  * If subscription id is provided, and subscription isn't found,
375
409
  * it'll throw an error.
376
410
  */
377
- subscriptionId: zod.z.string({
378
- description: "The id of the subscription to upgrade"
411
+ subscriptionId: z__namespace.string().meta({
412
+ description: 'The id of the subscription to upgrade. Eg: "sub_123"'
379
413
  }).optional(),
380
414
  /**
381
415
  * Any additional data you want to store in your database
382
416
  * subscriptions
383
417
  */
384
- metadata: zod.z.record(zod.z.string(), zod.z.any()).optional(),
418
+ metadata: z__namespace.record(z__namespace.string(), z__namespace.any()).optional(),
385
419
  /**
386
420
  * If a subscription
387
421
  */
388
- seats: zod.z.number({
389
- description: "Number of seats to upgrade to (if applicable)"
422
+ seats: z__namespace.number().meta({
423
+ description: "Number of seats to upgrade to (if applicable). Eg: 1"
390
424
  }).optional(),
391
425
  /**
392
426
  * Success URL to redirect back after successful subscription
393
427
  */
394
- successUrl: zod.z.string({
395
- description: "Callback URL to redirect back after successful subscription"
428
+ successUrl: z__namespace.string().meta({
429
+ description: 'Callback URL to redirect back after successful subscription. Eg: "https://example.com/success"'
396
430
  }).default("/"),
397
431
  /**
398
432
  * Cancel URL
399
433
  */
400
- cancelUrl: zod.z.string({
401
- description: "Callback URL to redirect back after successful subscription"
434
+ cancelUrl: z__namespace.string().meta({
435
+ description: 'Callback URL to redirect back after successful subscription. Eg: "https://example.com/success"'
402
436
  }).default("/"),
403
437
  /**
404
438
  * Return URL
405
439
  */
406
- returnUrl: zod.z.string().optional(),
440
+ returnUrl: z__namespace.string().meta({
441
+ description: 'Return URL to redirect back after successful subscription. Eg: "https://example.com/success"'
442
+ }).optional(),
407
443
  /**
408
444
  * Disable Redirect
409
445
  */
410
- disableRedirect: zod.z.boolean().default(false)
446
+ disableRedirect: z__namespace.boolean().meta({
447
+ description: "Disable redirect after successful subscription. Eg: true"
448
+ }).default(false)
411
449
  }),
412
450
  use: [
413
451
  api.sessionMiddleware,
@@ -454,19 +492,21 @@ const stripe = (options) => {
454
492
  let customerId = subscriptionToUpdate?.stripeCustomerId || user.stripeCustomerId;
455
493
  if (!customerId) {
456
494
  try {
457
- const stripeCustomer = await client.customers.create(
458
- {
495
+ const existingCustomers = await client.customers.list({
496
+ email: user.email,
497
+ limit: 1
498
+ });
499
+ let stripeCustomer = existingCustomers.data[0];
500
+ if (!stripeCustomer) {
501
+ stripeCustomer = await client.customers.create({
459
502
  email: user.email,
460
503
  name: user.name,
461
504
  metadata: {
462
505
  ...ctx.body.metadata,
463
506
  userId: user.id
464
507
  }
465
- },
466
- {
467
- idempotencyKey: crypto.generateRandomString(32, "a-z", "0-9")
468
- }
469
- );
508
+ });
509
+ }
470
510
  await ctx.context.adapter.update({
471
511
  model: "user",
472
512
  update: {
@@ -542,17 +582,45 @@ const stripe = (options) => {
542
582
  }
543
583
  let subscription = existingSubscription;
544
584
  if (!subscription) {
545
- const newSubscription = await ctx.context.adapter.create({
546
- model: "subscription",
547
- data: {
585
+ const incompleteSubscription = subscriptions.find(
586
+ (sub) => sub.status === "incomplete"
587
+ );
588
+ if (incompleteSubscription) {
589
+ await ctx.context.adapter.update({
590
+ model: "subscription",
591
+ update: {
592
+ ...incompleteSubscription,
593
+ plan: plan.name.toLowerCase(),
594
+ seats: ctx.body.seats || 1,
595
+ stripeCustomerId: customerId,
596
+ status: "active"
597
+ },
598
+ where: [
599
+ {
600
+ field: "id",
601
+ value: incompleteSubscription.id
602
+ }
603
+ ]
604
+ });
605
+ subscription = {
606
+ ...incompleteSubscription,
548
607
  plan: plan.name.toLowerCase(),
549
- stripeCustomerId: customerId,
550
- status: "incomplete",
551
- referenceId,
552
- seats: ctx.body.seats || 1
553
- }
554
- });
555
- subscription = newSubscription;
608
+ seats: ctx.body.seats || 1,
609
+ stripeCustomerId: customerId
610
+ };
611
+ } else {
612
+ const newSubscription = await ctx.context.adapter.create({
613
+ model: "subscription",
614
+ data: {
615
+ plan: plan.name.toLowerCase(),
616
+ stripeCustomerId: customerId,
617
+ status: "incomplete",
618
+ referenceId,
619
+ seats: ctx.body.seats || 1
620
+ }
621
+ });
622
+ subscription = newSubscription;
623
+ }
556
624
  }
557
625
  if (!subscription) {
558
626
  ctx.context.logger.error("Subscription ID not found");
@@ -565,7 +633,9 @@ const stripe = (options) => {
565
633
  plan,
566
634
  subscription
567
635
  },
568
- ctx.request
636
+ ctx.request,
637
+ //@ts-expect-error
638
+ ctx
569
639
  );
570
640
  const freeTrail = plan.freeTrial ? {
571
641
  trial_period_days: plan.freeTrial.days
@@ -642,7 +712,7 @@ const stripe = (options) => {
642
712
  "/subscription/cancel/callback",
643
713
  {
644
714
  method: "GET",
645
- query: zod.z.record(zod.z.string(), zod.z.any()).optional(),
715
+ query: z__namespace.record(z__namespace.string(), z__namespace.any()).optional(),
646
716
  use: [api.originCheck((ctx) => ctx.query.callbackURL)]
647
717
  },
648
718
  async (ctx) => {
@@ -709,14 +779,35 @@ const stripe = (options) => {
709
779
  throw ctx.redirect(getUrl(ctx, callbackURL));
710
780
  }
711
781
  ),
782
+ /**
783
+ * ### Endpoint
784
+ *
785
+ * POST `/subscription/cancel`
786
+ *
787
+ * ### API Methods
788
+ *
789
+ * **server:**
790
+ * `auth.api.cancelSubscription`
791
+ *
792
+ * **client:**
793
+ * `authClient.subscription.cancel`
794
+ *
795
+ * @see [Read our docs to learn more.](https://better-auth.com/docs/plugins/stripe#api-method-subscription-cancel)
796
+ */
712
797
  cancelSubscription: plugins.createAuthEndpoint(
713
798
  "/subscription/cancel",
714
799
  {
715
800
  method: "POST",
716
- body: zod.z.object({
717
- referenceId: zod.z.string().optional(),
718
- subscriptionId: zod.z.string().optional(),
719
- returnUrl: zod.z.string()
801
+ body: z__namespace.object({
802
+ referenceId: z__namespace.string().meta({
803
+ description: "Reference id of the subscription to cancel. Eg: '123'"
804
+ }).optional(),
805
+ subscriptionId: z__namespace.string().meta({
806
+ description: "The id of the subscription to cancel. Eg: 'sub_123'"
807
+ }).optional(),
808
+ returnUrl: z__namespace.string().meta({
809
+ description: "Return URL to redirect back after successful subscription. Eg: 'https://example.com/success'"
810
+ })
720
811
  }),
721
812
  use: [
722
813
  api.sessionMiddleware,
@@ -822,9 +913,13 @@ const stripe = (options) => {
822
913
  "/subscription/restore",
823
914
  {
824
915
  method: "POST",
825
- body: zod.z.object({
826
- referenceId: zod.z.string().optional(),
827
- subscriptionId: zod.z.string().optional()
916
+ body: z__namespace.object({
917
+ referenceId: z__namespace.string().meta({
918
+ description: "Reference id of the subscription to restore. Eg: '123'"
919
+ }).optional(),
920
+ subscriptionId: z__namespace.string().meta({
921
+ description: "The id of the subscription to restore. Eg: 'sub_123'"
922
+ }).optional()
828
923
  }),
829
924
  use: [api.sessionMiddleware, referenceMiddleware("restore-subscription")]
830
925
  },
@@ -907,13 +1002,30 @@ const stripe = (options) => {
907
1002
  }
908
1003
  }
909
1004
  ),
1005
+ /**
1006
+ * ### Endpoint
1007
+ *
1008
+ * GET `/subscription/list`
1009
+ *
1010
+ * ### API Methods
1011
+ *
1012
+ * **server:**
1013
+ * `auth.api.listActiveSubscriptions`
1014
+ *
1015
+ * **client:**
1016
+ * `authClient.subscription.list`
1017
+ *
1018
+ * @see [Read our docs to learn more.](https://better-auth.com/docs/plugins/stripe#api-method-subscription-list)
1019
+ */
910
1020
  listActiveSubscriptions: plugins.createAuthEndpoint(
911
1021
  "/subscription/list",
912
1022
  {
913
1023
  method: "GET",
914
- query: zod.z.optional(
915
- zod.z.object({
916
- referenceId: zod.z.string().optional()
1024
+ query: z__namespace.optional(
1025
+ z__namespace.object({
1026
+ referenceId: z__namespace.string().meta({
1027
+ description: "Reference id of the subscription to list. Eg: '123'"
1028
+ }).optional()
917
1029
  })
918
1030
  ),
919
1031
  use: [api.sessionMiddleware, referenceMiddleware("list-subscription")]
@@ -954,7 +1066,7 @@ const stripe = (options) => {
954
1066
  "/subscription/success",
955
1067
  {
956
1068
  method: "GET",
957
- query: zod.z.record(zod.z.string(), zod.z.any()).optional(),
1069
+ query: z__namespace.record(z__namespace.string(), z__namespace.any()).optional(),
958
1070
  use: [api.originCheck((ctx) => ctx.query.callbackURL)]
959
1071
  },
960
1072
  async (ctx) => {
@@ -1136,11 +1248,14 @@ const stripe = (options) => {
1136
1248
  if (!customer) {
1137
1249
  betterAuth.logger.error("#BETTER_AUTH: Failed to create customer");
1138
1250
  } else {
1139
- await options.onCustomerCreate?.({
1140
- customer,
1141
- stripeCustomer,
1142
- user
1143
- });
1251
+ await options.onCustomerCreate?.(
1252
+ {
1253
+ customer,
1254
+ stripeCustomer,
1255
+ user
1256
+ },
1257
+ ctx2
1258
+ );
1144
1259
  }
1145
1260
  }
1146
1261
  }