@better-auth/stripe 1.5.0-beta.18 → 1.5.0-beta.19

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/README.md ADDED
@@ -0,0 +1,17 @@
1
+ # Better Auth Stripe Plugin
2
+
3
+ Stripe plugin for [Better Auth](https://www.better-auth.com) — integrate Stripe billing with your authentication system.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @better-auth/stripe
9
+ ```
10
+
11
+ ## Documentation
12
+
13
+ For full documentation, visit [better-auth.com/docs/plugins/stripe](https://www.better-auth.com/docs/plugins/stripe).
14
+
15
+ ## License
16
+
17
+ MIT
package/package.json CHANGED
@@ -1,10 +1,8 @@
1
1
  {
2
2
  "name": "@better-auth/stripe",
3
- "author": "Bereket Engida",
4
- "version": "1.5.0-beta.18",
3
+ "version": "1.5.0-beta.19",
4
+ "description": "Stripe plugin for Better Auth",
5
5
  "type": "module",
6
- "main": "dist/index.mjs",
7
- "types": "dist/index.d.mts",
8
6
  "license": "MIT",
9
7
  "homepage": "https://www.better-auth.com/docs/plugins/stripe",
10
8
  "repository": {
@@ -12,16 +10,23 @@
12
10
  "url": "git+https://github.com/better-auth/better-auth.git",
13
11
  "directory": "packages/stripe"
14
12
  },
13
+ "author": "Bereket Engida",
15
14
  "keywords": [
16
15
  "stripe",
17
16
  "auth",
18
- "stripe"
17
+ "payments",
18
+ "typescript",
19
+ "better-auth"
19
20
  ],
20
- "module": "dist/index.mjs",
21
- "description": "Stripe plugin for Better Auth",
22
21
  "publishConfig": {
23
22
  "access": "public"
24
23
  },
24
+ "files": [
25
+ "dist"
26
+ ],
27
+ "main": "./dist/index.mjs",
28
+ "module": "./dist/index.mjs",
29
+ "types": "./dist/index.d.mts",
25
30
  "exports": {
26
31
  ".": {
27
32
  "dev-source": "./src/index.ts",
@@ -48,26 +53,26 @@
48
53
  "defu": "^6.1.4",
49
54
  "zod": "^4.3.6"
50
55
  },
51
- "peerDependencies": {
52
- "better-call": "1.3.2",
53
- "stripe": "^18 || ^19 || ^20",
54
- "@better-auth/core": "1.5.0-beta.18",
55
- "better-auth": "1.5.0-beta.18"
56
- },
57
56
  "devDependencies": {
58
57
  "better-call": "1.3.2",
59
58
  "stripe": "^20.2.0",
60
59
  "tsdown": "^0.20.3",
61
- "@better-auth/core": "1.5.0-beta.18",
62
- "better-auth": "1.5.0-beta.18"
60
+ "@better-auth/core": "1.5.0-beta.19",
61
+ "better-auth": "1.5.0-beta.19"
62
+ },
63
+ "peerDependencies": {
64
+ "better-call": "1.3.2",
65
+ "stripe": "^18 || ^19 || ^20",
66
+ "better-auth": "1.5.0-beta.19",
67
+ "@better-auth/core": "1.5.0-beta.19"
63
68
  },
64
69
  "scripts": {
65
- "test": "vitest",
66
- "coverage": "vitest run --coverage --coverage.provider=istanbul",
67
- "lint:package": "publint run --strict",
68
- "lint:types": "attw --profile esm-only --pack .",
69
70
  "build": "tsdown",
70
71
  "dev": "tsdown --watch",
71
- "typecheck": "tsc --project tsconfig.json"
72
+ "lint:package": "publint run --strict",
73
+ "lint:types": "attw --profile esm-only --pack .",
74
+ "typecheck": "tsc --project tsconfig.json",
75
+ "test": "vitest",
76
+ "coverage": "vitest run --coverage --coverage.provider=istanbul"
72
77
  }
73
78
  }
@@ -1,22 +0,0 @@
1
-
2
- > @better-auth/stripe@1.5.0-beta.18 build /home/runner/work/better-auth/better-auth/packages/stripe
3
- > tsdown
4
-
5
- ℹ tsdown v0.20.3 powered by rolldown v1.0.0-rc.3
6
- ℹ config file: /home/runner/work/better-auth/better-auth/packages/stripe/tsdown.config.ts
7
- ℹ entry: src/index.ts, src/client.ts
8
- ℹ tsconfig: tsconfig.json
9
- ℹ Build start
10
- ℹ dist/index.mjs  69.57 kB │ gzip: 13.19 kB
11
- ℹ dist/client.mjs  0.43 kB │ gzip: 0.29 kB
12
- ℹ dist/index.mjs.map 150.44 kB │ gzip: 29.40 kB
13
- ℹ dist/error-codes-CCosYkXx.mjs.map  2.43 kB │ gzip: 1.08 kB
14
- ℹ dist/error-codes-CCosYkXx.mjs  1.89 kB │ gzip: 0.85 kB
15
- ℹ dist/client.mjs.map  1.26 kB │ gzip: 0.59 kB
16
- ℹ dist/index.d.mts  19.78 kB │ gzip: 3.14 kB
17
- ℹ dist/client.d.mts  4.80 kB │ gzip: 0.93 kB
18
- ℹ dist/types-OT6L84x4.d.mts  12.92 kB │ gzip: 2.96 kB
19
- ℹ 9 files, total: 263.52 kB
20
- [PLUGIN_TIMINGS] Warning: Your build spent significant time in plugin `rolldown-plugin-dts:generate`. See https://rolldown.rs/options/checks#plugintimings for more details.
21
-
22
- ✔ Build complete in 43653ms
package/src/client.ts DELETED
@@ -1,37 +0,0 @@
1
- import type { BetterAuthClientPlugin } from "better-auth/client";
2
- import { STRIPE_ERROR_CODES } from "./error-codes";
3
- import type { StripePlan, stripe } from "./index";
4
- export const stripeClient = <
5
- O extends {
6
- subscription: boolean;
7
- },
8
- >(
9
- options?: O | undefined,
10
- ) => {
11
- return {
12
- id: "stripe-client",
13
- $InferServerPlugin: {} as ReturnType<
14
- typeof stripe<
15
- O["subscription"] extends true
16
- ? {
17
- stripeClient: any;
18
- stripeWebhookSecret: string;
19
- subscription: {
20
- enabled: true;
21
- plans: StripePlan[];
22
- };
23
- }
24
- : {
25
- stripeClient: any;
26
- stripeWebhookSecret: string;
27
- }
28
- >
29
- >,
30
- pathMethods: {
31
- "/subscription/billing-portal": "POST",
32
- "/subscription/restore": "POST",
33
- },
34
- $ERROR_CODES: STRIPE_ERROR_CODES,
35
- } satisfies BetterAuthClientPlugin;
36
- };
37
- export * from "./error-codes";
@@ -1,37 +0,0 @@
1
- import { defineErrorCodes } from "@better-auth/core/utils/error-codes";
2
-
3
- export const STRIPE_ERROR_CODES = defineErrorCodes({
4
- UNAUTHORIZED: "Unauthorized access",
5
- INVALID_REQUEST_BODY: "Invalid request body",
6
- SUBSCRIPTION_NOT_FOUND: "Subscription not found",
7
- SUBSCRIPTION_PLAN_NOT_FOUND: "Subscription plan not found",
8
- ALREADY_SUBSCRIBED_PLAN: "You're already subscribed to this plan",
9
- REFERENCE_ID_NOT_ALLOWED: "Reference id is not allowed",
10
- CUSTOMER_NOT_FOUND: "Stripe customer not found for this user",
11
- UNABLE_TO_CREATE_CUSTOMER: "Unable to create customer",
12
- UNABLE_TO_CREATE_BILLING_PORTAL: "Unable to create billing portal session",
13
- STRIPE_SIGNATURE_NOT_FOUND: "Stripe signature not found",
14
- STRIPE_WEBHOOK_SECRET_NOT_FOUND: "Stripe webhook secret not found",
15
- STRIPE_WEBHOOK_ERROR: "Stripe webhook error",
16
- FAILED_TO_CONSTRUCT_STRIPE_EVENT: "Failed to construct Stripe event",
17
- FAILED_TO_FETCH_PLANS: "Failed to fetch plans",
18
- EMAIL_VERIFICATION_REQUIRED:
19
- "Email verification is required before you can subscribe to a plan",
20
- SUBSCRIPTION_NOT_ACTIVE: "Subscription is not active",
21
- /**
22
- * @deprecated Use `SUBSCRIPTION_NOT_PENDING_CHANGE` instead.
23
- */
24
- SUBSCRIPTION_NOT_SCHEDULED_FOR_CANCELLATION:
25
- "Subscription is not scheduled for cancellation",
26
- SUBSCRIPTION_NOT_PENDING_CHANGE:
27
- "Subscription has no pending cancellation or scheduled plan change",
28
- ORGANIZATION_NOT_FOUND: "Organization not found",
29
- ORGANIZATION_SUBSCRIPTION_NOT_ENABLED:
30
- "Organization subscription is not enabled",
31
- AUTHORIZE_REFERENCE_REQUIRED:
32
- "Organization subscriptions require authorizeReference callback to be configured",
33
- ORGANIZATION_HAS_ACTIVE_SUBSCRIPTION:
34
- "Cannot delete organization with active subscription",
35
- ORGANIZATION_REFERENCE_ID_REQUIRED:
36
- "Reference ID is required. Provide referenceId or set activeOrganizationId in session",
37
- });
package/src/hooks.ts DELETED
@@ -1,479 +0,0 @@
1
- import type { GenericEndpointContext } from "@better-auth/core";
2
- import type { User } from "@better-auth/core/db";
3
- import type { Organization } from "better-auth/plugins/organization";
4
- import type Stripe from "stripe";
5
- import { subscriptionMetadata } from "./metadata";
6
- import type { CustomerType, StripeOptions, Subscription } from "./types";
7
- import {
8
- isActiveOrTrialing,
9
- isPendingCancel,
10
- isStripePendingCancel,
11
- resolvePlanItem,
12
- resolveQuantity,
13
- } from "./utils";
14
-
15
- /**
16
- * Find organization or user by stripeCustomerId.
17
- * @internal
18
- */
19
- async function findReferenceByStripeCustomerId(
20
- ctx: GenericEndpointContext,
21
- options: StripeOptions,
22
- stripeCustomerId: string,
23
- ): Promise<{ customerType: CustomerType; referenceId: string } | null> {
24
- if (options.organization?.enabled) {
25
- const org = await ctx.context.adapter.findOne<Organization>({
26
- model: "organization",
27
- where: [{ field: "stripeCustomerId", value: stripeCustomerId }],
28
- });
29
- if (org) return { customerType: "organization", referenceId: org.id };
30
- }
31
-
32
- const user = await ctx.context.adapter.findOne<User>({
33
- model: "user",
34
- where: [{ field: "stripeCustomerId", value: stripeCustomerId }],
35
- });
36
- if (user) return { customerType: "user", referenceId: user.id };
37
-
38
- return null;
39
- }
40
-
41
- export async function onCheckoutSessionCompleted(
42
- ctx: GenericEndpointContext,
43
- options: StripeOptions,
44
- event: Stripe.Event,
45
- ) {
46
- try {
47
- const client = options.stripeClient;
48
- const checkoutSession = event.data.object as Stripe.Checkout.Session;
49
- if (checkoutSession.mode === "setup" || !options.subscription?.enabled) {
50
- return;
51
- }
52
- const subscription = await client.subscriptions.retrieve(
53
- checkoutSession.subscription as string,
54
- );
55
- const resolved = await resolvePlanItem(options, subscription.items.data);
56
- if (!resolved) {
57
- ctx.context.logger.warn(
58
- `Stripe webhook warning: Subscription ${subscription.id} has no items matching a configured plan`,
59
- );
60
- return;
61
- }
62
-
63
- const { item: subscriptionItem, plan } = resolved;
64
- if (plan) {
65
- const checkoutMeta = subscriptionMetadata.get(checkoutSession?.metadata);
66
- const referenceId =
67
- checkoutSession?.client_reference_id || checkoutMeta.referenceId;
68
- const { subscriptionId } = checkoutMeta;
69
- const seats = resolveQuantity(
70
- subscription.items.data,
71
- subscriptionItem,
72
- plan.seatPriceId,
73
- );
74
- if (referenceId && subscriptionId) {
75
- const trial =
76
- subscription.trial_start && subscription.trial_end
77
- ? {
78
- trialStart: new Date(subscription.trial_start * 1000),
79
- trialEnd: new Date(subscription.trial_end * 1000),
80
- }
81
- : {};
82
-
83
- let dbSubscription = await ctx.context.adapter.update<Subscription>({
84
- model: "subscription",
85
- update: {
86
- ...trial,
87
- plan: plan.name.toLowerCase(),
88
- status: subscription.status,
89
- updatedAt: new Date(),
90
- periodStart: new Date(subscriptionItem.current_period_start * 1000),
91
- periodEnd: new Date(subscriptionItem.current_period_end * 1000),
92
- stripeSubscriptionId: checkoutSession.subscription as string,
93
- cancelAtPeriodEnd: subscription.cancel_at_period_end,
94
- cancelAt: subscription.cancel_at
95
- ? new Date(subscription.cancel_at * 1000)
96
- : null,
97
- canceledAt: subscription.canceled_at
98
- ? new Date(subscription.canceled_at * 1000)
99
- : null,
100
- endedAt: subscription.ended_at
101
- ? new Date(subscription.ended_at * 1000)
102
- : null,
103
- seats: seats,
104
- billingInterval: subscriptionItem.price.recurring?.interval,
105
- },
106
- where: [
107
- {
108
- field: "id",
109
- value: subscriptionId,
110
- },
111
- ],
112
- });
113
-
114
- if (trial.trialStart && plan.freeTrial?.onTrialStart) {
115
- await plan.freeTrial.onTrialStart(dbSubscription as Subscription);
116
- }
117
-
118
- if (!dbSubscription) {
119
- dbSubscription = await ctx.context.adapter.findOne<Subscription>({
120
- model: "subscription",
121
- where: [
122
- {
123
- field: "id",
124
- value: subscriptionId,
125
- },
126
- ],
127
- });
128
- }
129
- await options.subscription?.onSubscriptionComplete?.(
130
- {
131
- event,
132
- subscription: dbSubscription as Subscription,
133
- stripeSubscription: subscription,
134
- plan,
135
- },
136
- ctx,
137
- );
138
- return;
139
- }
140
- }
141
- } catch (e: any) {
142
- ctx.context.logger.error(`Stripe webhook failed. Error: ${e.message}`);
143
- }
144
- }
145
-
146
- export async function onSubscriptionCreated(
147
- ctx: GenericEndpointContext,
148
- options: StripeOptions,
149
- event: Stripe.Event,
150
- ) {
151
- try {
152
- if (!options.subscription?.enabled) {
153
- return;
154
- }
155
-
156
- const subscriptionCreated = event.data.object as Stripe.Subscription;
157
- const stripeCustomerId = subscriptionCreated.customer?.toString();
158
- if (!stripeCustomerId) {
159
- ctx.context.logger.warn(
160
- `Stripe webhook warning: customer.subscription.created event received without customer ID`,
161
- );
162
- return;
163
- }
164
-
165
- // Check if subscription already exists in database
166
- const { subscriptionId } = subscriptionMetadata.get(
167
- subscriptionCreated.metadata,
168
- );
169
- const existingSubscription =
170
- await ctx.context.adapter.findOne<Subscription>({
171
- model: "subscription",
172
- where: subscriptionId
173
- ? [{ field: "id", value: subscriptionId }]
174
- : [{ field: "stripeSubscriptionId", value: subscriptionCreated.id }], // Probably won't match since it's not set yet
175
- });
176
- if (existingSubscription) {
177
- ctx.context.logger.info(
178
- `Stripe webhook: Subscription already exists in database (id: ${existingSubscription.id}), skipping creation`,
179
- );
180
- return;
181
- }
182
-
183
- // Find reference
184
- const reference = await findReferenceByStripeCustomerId(
185
- ctx,
186
- options,
187
- stripeCustomerId,
188
- );
189
- if (!reference) {
190
- ctx.context.logger.warn(
191
- `Stripe webhook warning: No user or organization found with stripeCustomerId: ${stripeCustomerId}`,
192
- );
193
- return;
194
- }
195
- const { referenceId, customerType } = reference;
196
-
197
- const resolved = await resolvePlanItem(
198
- options,
199
- subscriptionCreated.items.data,
200
- );
201
- if (!resolved) {
202
- ctx.context.logger.warn(
203
- `Stripe webhook warning: Subscription ${subscriptionCreated.id} has no items matching a configured plan`,
204
- );
205
- return;
206
- }
207
-
208
- const { item: subscriptionItem, plan } = resolved;
209
- if (!plan) {
210
- ctx.context.logger.warn(
211
- `Stripe webhook warning: No matching plan found for priceId: ${subscriptionItem.price.id}`,
212
- );
213
- return;
214
- }
215
-
216
- const seats = resolveQuantity(
217
- subscriptionCreated.items.data,
218
- subscriptionItem,
219
- plan.seatPriceId,
220
- );
221
- const periodStart = new Date(subscriptionItem.current_period_start * 1000);
222
- const periodEnd = new Date(subscriptionItem.current_period_end * 1000);
223
-
224
- const trial =
225
- subscriptionCreated.trial_start && subscriptionCreated.trial_end
226
- ? {
227
- trialStart: new Date(subscriptionCreated.trial_start * 1000),
228
- trialEnd: new Date(subscriptionCreated.trial_end * 1000),
229
- }
230
- : {};
231
-
232
- // Create the subscription in the database
233
- const newSubscription = await ctx.context.adapter.create<Subscription>({
234
- model: "subscription",
235
- data: {
236
- ...trial,
237
- ...(plan.limits ? { limits: plan.limits } : {}),
238
- referenceId,
239
- stripeCustomerId,
240
- stripeSubscriptionId: subscriptionCreated.id,
241
- status: subscriptionCreated.status,
242
- plan: plan.name.toLowerCase(),
243
- periodStart,
244
- periodEnd,
245
- seats,
246
- billingInterval: subscriptionItem.price.recurring?.interval,
247
- },
248
- });
249
-
250
- ctx.context.logger.info(
251
- `Stripe webhook: Created subscription ${subscriptionCreated.id} for ${customerType} ${referenceId} from dashboard`,
252
- );
253
-
254
- await options.subscription.onSubscriptionCreated?.({
255
- event,
256
- subscription: newSubscription,
257
- stripeSubscription: subscriptionCreated,
258
- plan,
259
- });
260
- } catch (error: any) {
261
- ctx.context.logger.error(`Stripe webhook failed. Error: ${error}`);
262
- }
263
- }
264
-
265
- export async function onSubscriptionUpdated(
266
- ctx: GenericEndpointContext,
267
- options: StripeOptions,
268
- event: Stripe.Event,
269
- ) {
270
- try {
271
- if (!options.subscription?.enabled) {
272
- return;
273
- }
274
- const subscriptionUpdated = event.data.object as Stripe.Subscription;
275
- const resolved = await resolvePlanItem(
276
- options,
277
- subscriptionUpdated.items.data,
278
- );
279
- if (!resolved) {
280
- ctx.context.logger.warn(
281
- `Stripe webhook warning: Subscription ${subscriptionUpdated.id} has no items matching a configured plan`,
282
- );
283
- return;
284
- }
285
-
286
- const { item: subscriptionItem, plan } = resolved;
287
-
288
- const { subscriptionId } = subscriptionMetadata.get(
289
- subscriptionUpdated.metadata,
290
- );
291
- const customerId = subscriptionUpdated.customer?.toString();
292
- let subscription = await ctx.context.adapter.findOne<Subscription>({
293
- model: "subscription",
294
- where: subscriptionId
295
- ? [{ field: "id", value: subscriptionId }]
296
- : [{ field: "stripeSubscriptionId", value: subscriptionUpdated.id }],
297
- });
298
- if (!subscription) {
299
- const subs = await ctx.context.adapter.findMany<Subscription>({
300
- model: "subscription",
301
- where: [{ field: "stripeCustomerId", value: customerId }],
302
- });
303
- if (subs.length > 1) {
304
- const activeSub = subs.find((sub: Subscription) =>
305
- isActiveOrTrialing(sub),
306
- );
307
- if (!activeSub) {
308
- ctx.context.logger.warn(
309
- `Stripe webhook error: Multiple subscriptions found for customerId: ${customerId} and no active subscription is found`,
310
- );
311
- return;
312
- }
313
- subscription = activeSub;
314
- } else {
315
- subscription = subs[0]!;
316
- }
317
- }
318
-
319
- const seats = plan
320
- ? resolveQuantity(
321
- subscriptionUpdated.items.data,
322
- subscriptionItem,
323
- plan.seatPriceId,
324
- )
325
- : subscriptionItem.quantity;
326
-
327
- const trial =
328
- subscriptionUpdated.trial_start && subscriptionUpdated.trial_end
329
- ? {
330
- trialStart: new Date(subscriptionUpdated.trial_start * 1000),
331
- trialEnd: new Date(subscriptionUpdated.trial_end * 1000),
332
- }
333
- : {};
334
-
335
- const updatedSubscription = await ctx.context.adapter.update<Subscription>({
336
- model: "subscription",
337
- update: {
338
- ...trial,
339
- ...(plan
340
- ? {
341
- plan: plan.name.toLowerCase(),
342
- limits: plan.limits,
343
- }
344
- : {}),
345
- updatedAt: new Date(),
346
- status: subscriptionUpdated.status,
347
- periodStart: new Date(subscriptionItem.current_period_start * 1000),
348
- periodEnd: new Date(subscriptionItem.current_period_end * 1000),
349
- cancelAtPeriodEnd: subscriptionUpdated.cancel_at_period_end,
350
- cancelAt: subscriptionUpdated.cancel_at
351
- ? new Date(subscriptionUpdated.cancel_at * 1000)
352
- : null,
353
- canceledAt: subscriptionUpdated.canceled_at
354
- ? new Date(subscriptionUpdated.canceled_at * 1000)
355
- : null,
356
- endedAt: subscriptionUpdated.ended_at
357
- ? new Date(subscriptionUpdated.ended_at * 1000)
358
- : null,
359
- seats,
360
- stripeSubscriptionId: subscriptionUpdated.id,
361
- billingInterval: subscriptionItem.price.recurring?.interval,
362
- stripeScheduleId: subscriptionUpdated.schedule
363
- ? typeof subscriptionUpdated.schedule === "string"
364
- ? subscriptionUpdated.schedule
365
- : subscriptionUpdated.schedule.id
366
- : null,
367
- },
368
- where: [
369
- {
370
- field: "id",
371
- value: subscription.id,
372
- },
373
- ],
374
- });
375
- const isNewCancellation =
376
- subscriptionUpdated.status === "active" &&
377
- isStripePendingCancel(subscriptionUpdated) &&
378
- !isPendingCancel(subscription);
379
- if (isNewCancellation) {
380
- await options.subscription.onSubscriptionCancel?.({
381
- subscription,
382
- cancellationDetails:
383
- subscriptionUpdated.cancellation_details || undefined,
384
- stripeSubscription: subscriptionUpdated,
385
- event,
386
- });
387
- }
388
- await options.subscription.onSubscriptionUpdate?.({
389
- event,
390
- subscription: updatedSubscription || subscription,
391
- });
392
- if (plan) {
393
- if (
394
- subscriptionUpdated.status === "active" &&
395
- subscription.status === "trialing" &&
396
- plan.freeTrial?.onTrialEnd
397
- ) {
398
- await plan.freeTrial.onTrialEnd({ subscription }, ctx);
399
- }
400
- if (
401
- subscriptionUpdated.status === "incomplete_expired" &&
402
- subscription.status === "trialing" &&
403
- plan.freeTrial?.onTrialExpired
404
- ) {
405
- await plan.freeTrial.onTrialExpired(subscription, ctx);
406
- }
407
- }
408
- } catch (error: any) {
409
- ctx.context.logger.error(`Stripe webhook failed. Error: ${error}`);
410
- }
411
- }
412
-
413
- export async function onSubscriptionDeleted(
414
- ctx: GenericEndpointContext,
415
- options: StripeOptions,
416
- event: Stripe.Event,
417
- ) {
418
- if (!options.subscription?.enabled) {
419
- return;
420
- }
421
- try {
422
- const subscriptionDeleted = event.data.object as Stripe.Subscription;
423
- const subscriptionId = subscriptionDeleted.id;
424
- const subscription = await ctx.context.adapter.findOne<Subscription>({
425
- model: "subscription",
426
- where: [
427
- {
428
- field: "stripeSubscriptionId",
429
- value: subscriptionId,
430
- },
431
- ],
432
- });
433
- if (subscription) {
434
- const trial =
435
- subscriptionDeleted.trial_start && subscriptionDeleted.trial_end
436
- ? {
437
- trialStart: new Date(subscriptionDeleted.trial_start * 1000),
438
- trialEnd: new Date(subscriptionDeleted.trial_end * 1000),
439
- }
440
- : {};
441
- await ctx.context.adapter.update({
442
- model: "subscription",
443
- where: [
444
- {
445
- field: "id",
446
- value: subscription.id,
447
- },
448
- ],
449
- update: {
450
- ...trial,
451
- status: "canceled",
452
- updatedAt: new Date(),
453
- cancelAtPeriodEnd: subscriptionDeleted.cancel_at_period_end,
454
- cancelAt: subscriptionDeleted.cancel_at
455
- ? new Date(subscriptionDeleted.cancel_at * 1000)
456
- : null,
457
- canceledAt: subscriptionDeleted.canceled_at
458
- ? new Date(subscriptionDeleted.canceled_at * 1000)
459
- : null,
460
- endedAt: subscriptionDeleted.ended_at
461
- ? new Date(subscriptionDeleted.ended_at * 1000)
462
- : null,
463
- stripeScheduleId: null,
464
- },
465
- });
466
- await options.subscription.onSubscriptionDeleted?.({
467
- event,
468
- stripeSubscription: subscriptionDeleted,
469
- subscription,
470
- });
471
- } else {
472
- ctx.context.logger.warn(
473
- `Stripe webhook error: Subscription not found for subscriptionId: ${subscriptionId}`,
474
- );
475
- }
476
- } catch (error: any) {
477
- ctx.context.logger.error(`Stripe webhook failed. Error: ${error}`);
478
- }
479
- }