@better-auth/stripe 1.5.0-beta.1 → 1.5.0-beta.3
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 +9 -9
- package/CHANGELOG.md +15 -13
- package/LICENSE.md +15 -12
- package/dist/client.d.mts +106 -1
- package/dist/client.mjs +5 -2
- package/dist/error-codes-Bkj5yJMT.mjs +29 -0
- package/dist/{index-DpiQGYLJ.d.mts → index-BnHmwMru.d.mts} +314 -155
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +628 -231
- package/package.json +6 -6
- package/src/client.ts +1 -0
- package/src/error-codes.ts +16 -0
- package/src/hooks.ts +229 -53
- package/src/index.ts +142 -45
- package/src/middleware.ts +91 -45
- package/src/routes.ts +620 -300
- package/src/schema.ts +30 -0
- package/src/types.ts +105 -20
- package/src/utils.ts +36 -1
- package/test/stripe-organization.test.ts +1993 -0
- package/{src → test}/stripe.test.ts +3350 -1404
- package/dist/error-codes-qqooUh6R.mjs +0 -16
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@better-auth/stripe",
|
|
3
3
|
"author": "Bereket Engida",
|
|
4
|
-
"version": "1.5.0-beta.
|
|
4
|
+
"version": "1.5.0-beta.3",
|
|
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.5.0-beta.
|
|
54
|
-
"better-auth": "1.5.0-beta.
|
|
53
|
+
"@better-auth/core": "1.5.0-beta.3",
|
|
54
|
+
"better-auth": "1.5.0-beta.3"
|
|
55
55
|
},
|
|
56
56
|
"devDependencies": {
|
|
57
|
-
"better-call": "1.1.
|
|
57
|
+
"better-call": "1.1.8",
|
|
58
58
|
"stripe": "^20.0.0",
|
|
59
59
|
"tsdown": "^0.17.2",
|
|
60
|
-
"@better-auth/core": "1.5.0-beta.
|
|
61
|
-
"better-auth": "1.5.0-beta.
|
|
60
|
+
"@better-auth/core": "1.5.0-beta.3",
|
|
61
|
+
"better-auth": "1.5.0-beta.3"
|
|
62
62
|
},
|
|
63
63
|
"scripts": {
|
|
64
64
|
"test": "vitest",
|
package/src/client.ts
CHANGED
package/src/error-codes.ts
CHANGED
|
@@ -1,14 +1,30 @@
|
|
|
1
1
|
import { defineErrorCodes } from "@better-auth/core/utils";
|
|
2
2
|
|
|
3
3
|
export const STRIPE_ERROR_CODES = defineErrorCodes({
|
|
4
|
+
UNAUTHORIZED: "Unauthorized access",
|
|
5
|
+
INVALID_REQUEST_BODY: "Invalid request body",
|
|
4
6
|
SUBSCRIPTION_NOT_FOUND: "Subscription not found",
|
|
5
7
|
SUBSCRIPTION_PLAN_NOT_FOUND: "Subscription plan not found",
|
|
6
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",
|
|
7
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",
|
|
8
17
|
FAILED_TO_FETCH_PLANS: "Failed to fetch plans",
|
|
9
18
|
EMAIL_VERIFICATION_REQUIRED:
|
|
10
19
|
"Email verification is required before you can subscribe to a plan",
|
|
11
20
|
SUBSCRIPTION_NOT_ACTIVE: "Subscription is not active",
|
|
12
21
|
SUBSCRIPTION_NOT_SCHEDULED_FOR_CANCELLATION:
|
|
13
22
|
"Subscription is not scheduled for cancellation",
|
|
23
|
+
ORGANIZATION_NOT_FOUND: "Organization not found",
|
|
24
|
+
ORGANIZATION_SUBSCRIPTION_NOT_ENABLED:
|
|
25
|
+
"Organization subscription is not enabled",
|
|
26
|
+
ORGANIZATION_HAS_ACTIVE_SUBSCRIPTION:
|
|
27
|
+
"Cannot delete organization with active subscription",
|
|
28
|
+
ORGANIZATION_REFERENCE_ID_REQUIRED:
|
|
29
|
+
"Reference ID is required. Provide referenceId or set activeOrganizationId in session",
|
|
14
30
|
});
|
package/src/hooks.ts
CHANGED
|
@@ -1,8 +1,40 @@
|
|
|
1
|
-
import type { GenericEndpointContext } from "better-auth";
|
|
2
|
-
import {
|
|
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";
|
|
3
4
|
import type Stripe from "stripe";
|
|
4
|
-
import type {
|
|
5
|
-
import {
|
|
5
|
+
import type { CustomerType, StripeOptions, Subscription } from "./types";
|
|
6
|
+
import {
|
|
7
|
+
getPlanByPriceInfo,
|
|
8
|
+
isActiveOrTrialing,
|
|
9
|
+
isPendingCancel,
|
|
10
|
+
isStripePendingCancel,
|
|
11
|
+
} from "./utils";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Find organization or user by stripeCustomerId.
|
|
15
|
+
* @internal
|
|
16
|
+
*/
|
|
17
|
+
async function findReferenceByStripeCustomerId(
|
|
18
|
+
ctx: GenericEndpointContext,
|
|
19
|
+
options: StripeOptions,
|
|
20
|
+
stripeCustomerId: string,
|
|
21
|
+
): Promise<{ customerType: CustomerType; referenceId: string } | null> {
|
|
22
|
+
if (options.organization?.enabled) {
|
|
23
|
+
const org = await ctx.context.adapter.findOne<Organization>({
|
|
24
|
+
model: "organization",
|
|
25
|
+
where: [{ field: "stripeCustomerId", value: stripeCustomerId }],
|
|
26
|
+
});
|
|
27
|
+
if (org) return { customerType: "organization", referenceId: org.id };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const user = await ctx.context.adapter.findOne<User>({
|
|
31
|
+
model: "user",
|
|
32
|
+
where: [{ field: "stripeCustomerId", value: stripeCustomerId }],
|
|
33
|
+
});
|
|
34
|
+
if (user) return { customerType: "user", referenceId: user.id };
|
|
35
|
+
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
6
38
|
|
|
7
39
|
export async function onCheckoutSessionCompleted(
|
|
8
40
|
ctx: GenericEndpointContext,
|
|
@@ -18,8 +50,16 @@ export async function onCheckoutSessionCompleted(
|
|
|
18
50
|
const subscription = await client.subscriptions.retrieve(
|
|
19
51
|
checkoutSession.subscription as string,
|
|
20
52
|
);
|
|
21
|
-
const
|
|
22
|
-
|
|
53
|
+
const subscriptionItem = subscription.items.data[0];
|
|
54
|
+
if (!subscriptionItem) {
|
|
55
|
+
ctx.context.logger.warn(
|
|
56
|
+
`Stripe webhook warning: Subscription ${subscription.id} has no items`,
|
|
57
|
+
);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const priceId = subscriptionItem.price.id;
|
|
62
|
+
const priceLookupKey = subscriptionItem.price.lookup_key;
|
|
23
63
|
const plan = await getPlanByPriceInfo(
|
|
24
64
|
options,
|
|
25
65
|
priceId as string,
|
|
@@ -30,7 +70,7 @@ export async function onCheckoutSessionCompleted(
|
|
|
30
70
|
checkoutSession?.client_reference_id ||
|
|
31
71
|
checkoutSession?.metadata?.referenceId;
|
|
32
72
|
const subscriptionId = checkoutSession?.metadata?.subscriptionId;
|
|
33
|
-
const seats =
|
|
73
|
+
const seats = subscriptionItem.quantity;
|
|
34
74
|
if (referenceId && subscriptionId) {
|
|
35
75
|
const trial =
|
|
36
76
|
subscription.trial_start && subscription.trial_end
|
|
@@ -40,30 +80,35 @@ export async function onCheckoutSessionCompleted(
|
|
|
40
80
|
}
|
|
41
81
|
: {};
|
|
42
82
|
|
|
43
|
-
let dbSubscription =
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
83
|
+
let dbSubscription = await ctx.context.adapter.update<Subscription>({
|
|
84
|
+
model: "subscription",
|
|
85
|
+
update: {
|
|
86
|
+
plan: plan.name.toLowerCase(),
|
|
87
|
+
status: subscription.status,
|
|
88
|
+
updatedAt: new Date(),
|
|
89
|
+
periodStart: new Date(subscriptionItem.current_period_start * 1000),
|
|
90
|
+
periodEnd: new Date(subscriptionItem.current_period_end * 1000),
|
|
91
|
+
stripeSubscriptionId: checkoutSession.subscription as string,
|
|
92
|
+
cancelAtPeriodEnd: subscription.cancel_at_period_end,
|
|
93
|
+
cancelAt: subscription.cancel_at
|
|
94
|
+
? new Date(subscription.cancel_at * 1000)
|
|
95
|
+
: null,
|
|
96
|
+
canceledAt: subscription.canceled_at
|
|
97
|
+
? new Date(subscription.canceled_at * 1000)
|
|
98
|
+
: null,
|
|
99
|
+
endedAt: subscription.ended_at
|
|
100
|
+
? new Date(subscription.ended_at * 1000)
|
|
101
|
+
: null,
|
|
102
|
+
seats: seats,
|
|
103
|
+
...trial,
|
|
104
|
+
},
|
|
105
|
+
where: [
|
|
106
|
+
{
|
|
107
|
+
field: "id",
|
|
108
|
+
value: subscriptionId,
|
|
59
109
|
},
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
field: "id",
|
|
63
|
-
value: subscriptionId,
|
|
64
|
-
},
|
|
65
|
-
],
|
|
66
|
-
});
|
|
110
|
+
],
|
|
111
|
+
});
|
|
67
112
|
|
|
68
113
|
if (trial.trialStart && plan.freeTrial?.onTrialStart) {
|
|
69
114
|
await plan.freeTrial.onTrialStart(dbSubscription as Subscription);
|
|
@@ -93,7 +138,118 @@ export async function onCheckoutSessionCompleted(
|
|
|
93
138
|
}
|
|
94
139
|
}
|
|
95
140
|
} catch (e: any) {
|
|
96
|
-
logger.error(`Stripe webhook failed. Error: ${e.message}`);
|
|
141
|
+
ctx.context.logger.error(`Stripe webhook failed. Error: ${e.message}`);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export async function onSubscriptionCreated(
|
|
146
|
+
ctx: GenericEndpointContext,
|
|
147
|
+
options: StripeOptions,
|
|
148
|
+
event: Stripe.Event,
|
|
149
|
+
) {
|
|
150
|
+
try {
|
|
151
|
+
if (!options.subscription?.enabled) {
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const subscriptionCreated = event.data.object as Stripe.Subscription;
|
|
156
|
+
const stripeCustomerId = subscriptionCreated.customer?.toString();
|
|
157
|
+
if (!stripeCustomerId) {
|
|
158
|
+
ctx.context.logger.warn(
|
|
159
|
+
`Stripe webhook warning: customer.subscription.created event received without customer ID`,
|
|
160
|
+
);
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Check if subscription already exists in database
|
|
165
|
+
const subscriptionId = subscriptionCreated.metadata?.subscriptionId;
|
|
166
|
+
const existingSubscription =
|
|
167
|
+
await ctx.context.adapter.findOne<Subscription>({
|
|
168
|
+
model: "subscription",
|
|
169
|
+
where: subscriptionId
|
|
170
|
+
? [{ field: "id", value: subscriptionId }]
|
|
171
|
+
: [{ field: "stripeSubscriptionId", value: subscriptionCreated.id }], // Probably won't match since it's not set yet
|
|
172
|
+
});
|
|
173
|
+
if (existingSubscription) {
|
|
174
|
+
ctx.context.logger.info(
|
|
175
|
+
`Stripe webhook: Subscription already exists in database (id: ${existingSubscription.id}), skipping creation`,
|
|
176
|
+
);
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Find reference
|
|
181
|
+
const reference = await findReferenceByStripeCustomerId(
|
|
182
|
+
ctx,
|
|
183
|
+
options,
|
|
184
|
+
stripeCustomerId,
|
|
185
|
+
);
|
|
186
|
+
if (!reference) {
|
|
187
|
+
ctx.context.logger.warn(
|
|
188
|
+
`Stripe webhook warning: No user or organization found with stripeCustomerId: ${stripeCustomerId}`,
|
|
189
|
+
);
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
const { referenceId, customerType } = reference;
|
|
193
|
+
|
|
194
|
+
const subscriptionItem = subscriptionCreated.items.data[0];
|
|
195
|
+
if (!subscriptionItem) {
|
|
196
|
+
ctx.context.logger.warn(
|
|
197
|
+
`Stripe webhook warning: Subscription ${subscriptionCreated.id} has no items`,
|
|
198
|
+
);
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const priceId = subscriptionItem.price.id;
|
|
203
|
+
const priceLookupKey = subscriptionItem.price.lookup_key || null;
|
|
204
|
+
const plan = await getPlanByPriceInfo(options, priceId, priceLookupKey);
|
|
205
|
+
if (!plan) {
|
|
206
|
+
ctx.context.logger.warn(
|
|
207
|
+
`Stripe webhook warning: No matching plan found for priceId: ${priceId}`,
|
|
208
|
+
);
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const seats = subscriptionItem.quantity;
|
|
213
|
+
const periodStart = new Date(subscriptionItem.current_period_start * 1000);
|
|
214
|
+
const periodEnd = new Date(subscriptionItem.current_period_end * 1000);
|
|
215
|
+
|
|
216
|
+
const trial =
|
|
217
|
+
subscriptionCreated.trial_start && subscriptionCreated.trial_end
|
|
218
|
+
? {
|
|
219
|
+
trialStart: new Date(subscriptionCreated.trial_start * 1000),
|
|
220
|
+
trialEnd: new Date(subscriptionCreated.trial_end * 1000),
|
|
221
|
+
}
|
|
222
|
+
: {};
|
|
223
|
+
|
|
224
|
+
// Create the subscription in the database
|
|
225
|
+
const newSubscription = await ctx.context.adapter.create<Subscription>({
|
|
226
|
+
model: "subscription",
|
|
227
|
+
data: {
|
|
228
|
+
referenceId,
|
|
229
|
+
stripeCustomerId,
|
|
230
|
+
stripeSubscriptionId: subscriptionCreated.id,
|
|
231
|
+
status: subscriptionCreated.status,
|
|
232
|
+
plan: plan.name.toLowerCase(),
|
|
233
|
+
periodStart,
|
|
234
|
+
periodEnd,
|
|
235
|
+
seats,
|
|
236
|
+
...(plan.limits ? { limits: plan.limits } : {}),
|
|
237
|
+
...trial,
|
|
238
|
+
},
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
ctx.context.logger.info(
|
|
242
|
+
`Stripe webhook: Created subscription ${subscriptionCreated.id} for ${customerType} ${referenceId} from dashboard`,
|
|
243
|
+
);
|
|
244
|
+
|
|
245
|
+
await options.subscription.onSubscriptionCreated?.({
|
|
246
|
+
event,
|
|
247
|
+
subscription: newSubscription,
|
|
248
|
+
stripeSubscription: subscriptionCreated,
|
|
249
|
+
plan,
|
|
250
|
+
});
|
|
251
|
+
} catch (error: any) {
|
|
252
|
+
ctx.context.logger.error(`Stripe webhook failed. Error: ${error}`);
|
|
97
253
|
}
|
|
98
254
|
}
|
|
99
255
|
|
|
@@ -107,9 +263,16 @@ export async function onSubscriptionUpdated(
|
|
|
107
263
|
return;
|
|
108
264
|
}
|
|
109
265
|
const subscriptionUpdated = event.data.object as Stripe.Subscription;
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
|
|
266
|
+
const subscriptionItem = subscriptionUpdated.items.data[0];
|
|
267
|
+
if (!subscriptionItem) {
|
|
268
|
+
ctx.context.logger.warn(
|
|
269
|
+
`Stripe webhook warning: Subscription ${subscriptionUpdated.id} has no items`,
|
|
270
|
+
);
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
const priceId = subscriptionItem.price.id;
|
|
275
|
+
const priceLookupKey = subscriptionItem.price.lookup_key;
|
|
113
276
|
const plan = await getPlanByPriceInfo(options, priceId, priceLookupKey);
|
|
114
277
|
|
|
115
278
|
const subscriptionId = subscriptionUpdated.metadata?.subscriptionId;
|
|
@@ -126,12 +289,11 @@ export async function onSubscriptionUpdated(
|
|
|
126
289
|
where: [{ field: "stripeCustomerId", value: customerId }],
|
|
127
290
|
});
|
|
128
291
|
if (subs.length > 1) {
|
|
129
|
-
const activeSub = subs.find(
|
|
130
|
-
(sub
|
|
131
|
-
sub.status === "active" || sub.status === "trialing",
|
|
292
|
+
const activeSub = subs.find((sub: Subscription) =>
|
|
293
|
+
isActiveOrTrialing(sub),
|
|
132
294
|
);
|
|
133
295
|
if (!activeSub) {
|
|
134
|
-
logger.warn(
|
|
296
|
+
ctx.context.logger.warn(
|
|
135
297
|
`Stripe webhook error: Multiple subscriptions found for customerId: ${customerId} and no active subscription is found`,
|
|
136
298
|
);
|
|
137
299
|
return;
|
|
@@ -142,7 +304,6 @@ export async function onSubscriptionUpdated(
|
|
|
142
304
|
}
|
|
143
305
|
}
|
|
144
306
|
|
|
145
|
-
const seats = subscriptionUpdated.items.data[0]!.quantity;
|
|
146
307
|
const updatedSubscription = await ctx.context.adapter.update<Subscription>({
|
|
147
308
|
model: "subscription",
|
|
148
309
|
update: {
|
|
@@ -154,14 +315,19 @@ export async function onSubscriptionUpdated(
|
|
|
154
315
|
: {}),
|
|
155
316
|
updatedAt: new Date(),
|
|
156
317
|
status: subscriptionUpdated.status,
|
|
157
|
-
periodStart: new Date(
|
|
158
|
-
|
|
159
|
-
),
|
|
160
|
-
periodEnd: new Date(
|
|
161
|
-
subscriptionUpdated.items.data[0]!.current_period_end * 1000,
|
|
162
|
-
),
|
|
318
|
+
periodStart: new Date(subscriptionItem.current_period_start * 1000),
|
|
319
|
+
periodEnd: new Date(subscriptionItem.current_period_end * 1000),
|
|
163
320
|
cancelAtPeriodEnd: subscriptionUpdated.cancel_at_period_end,
|
|
164
|
-
|
|
321
|
+
cancelAt: subscriptionUpdated.cancel_at
|
|
322
|
+
? new Date(subscriptionUpdated.cancel_at * 1000)
|
|
323
|
+
: null,
|
|
324
|
+
canceledAt: subscriptionUpdated.canceled_at
|
|
325
|
+
? new Date(subscriptionUpdated.canceled_at * 1000)
|
|
326
|
+
: null,
|
|
327
|
+
endedAt: subscriptionUpdated.ended_at
|
|
328
|
+
? new Date(subscriptionUpdated.ended_at * 1000)
|
|
329
|
+
: null,
|
|
330
|
+
seats: subscriptionItem.quantity,
|
|
165
331
|
stripeSubscriptionId: subscriptionUpdated.id,
|
|
166
332
|
},
|
|
167
333
|
where: [
|
|
@@ -171,11 +337,11 @@ export async function onSubscriptionUpdated(
|
|
|
171
337
|
},
|
|
172
338
|
],
|
|
173
339
|
});
|
|
174
|
-
const
|
|
340
|
+
const isNewCancellation =
|
|
175
341
|
subscriptionUpdated.status === "active" &&
|
|
176
|
-
subscriptionUpdated
|
|
177
|
-
!subscription
|
|
178
|
-
if (
|
|
342
|
+
isStripePendingCancel(subscriptionUpdated) &&
|
|
343
|
+
!isPendingCancel(subscription);
|
|
344
|
+
if (isNewCancellation) {
|
|
179
345
|
await options.subscription.onSubscriptionCancel?.({
|
|
180
346
|
subscription,
|
|
181
347
|
cancellationDetails:
|
|
@@ -205,7 +371,7 @@ export async function onSubscriptionUpdated(
|
|
|
205
371
|
}
|
|
206
372
|
}
|
|
207
373
|
} catch (error: any) {
|
|
208
|
-
logger.error(`Stripe webhook failed. Error: ${error}`);
|
|
374
|
+
ctx.context.logger.error(`Stripe webhook failed. Error: ${error}`);
|
|
209
375
|
}
|
|
210
376
|
}
|
|
211
377
|
|
|
@@ -241,6 +407,16 @@ export async function onSubscriptionDeleted(
|
|
|
241
407
|
update: {
|
|
242
408
|
status: "canceled",
|
|
243
409
|
updatedAt: new Date(),
|
|
410
|
+
cancelAtPeriodEnd: subscriptionDeleted.cancel_at_period_end,
|
|
411
|
+
cancelAt: subscriptionDeleted.cancel_at
|
|
412
|
+
? new Date(subscriptionDeleted.cancel_at * 1000)
|
|
413
|
+
: null,
|
|
414
|
+
canceledAt: subscriptionDeleted.canceled_at
|
|
415
|
+
? new Date(subscriptionDeleted.canceled_at * 1000)
|
|
416
|
+
: null,
|
|
417
|
+
endedAt: subscriptionDeleted.ended_at
|
|
418
|
+
? new Date(subscriptionDeleted.ended_at * 1000)
|
|
419
|
+
: null,
|
|
244
420
|
},
|
|
245
421
|
});
|
|
246
422
|
await options.subscription.onSubscriptionDeleted?.({
|
|
@@ -249,11 +425,11 @@ export async function onSubscriptionDeleted(
|
|
|
249
425
|
subscription,
|
|
250
426
|
});
|
|
251
427
|
} else {
|
|
252
|
-
logger.warn(
|
|
428
|
+
ctx.context.logger.warn(
|
|
253
429
|
`Stripe webhook error: Subscription not found for subscriptionId: ${subscriptionId}`,
|
|
254
430
|
);
|
|
255
431
|
}
|
|
256
432
|
} catch (error: any) {
|
|
257
|
-
logger.error(`Stripe webhook failed. Error: ${error}`);
|
|
433
|
+
ctx.context.logger.error(`Stripe webhook failed. Error: ${error}`);
|
|
258
434
|
}
|
|
259
435
|
}
|