fix(stripe): pass post-update subscription to onSubscriptionDeleted and trial callbacks (#9356)

This commit is contained in:
Taesu
2026-04-26 19:26:56 +09:00
committed by GitHub
parent 012b4e6331
commit 3e4fc8ca74
2 changed files with 43 additions and 27 deletions

View File

@@ -0,0 +1,5 @@
---
"@better-auth/stripe": patch
---
`onSubscriptionDeleted`, `onTrialEnd`, and `onTrialExpired` now receive the post-update subscription row instead of the pre-update snapshot, consistent with the rest of the lifecycle callbacks.

View File

@@ -416,14 +416,17 @@ export async function onSubscriptionUpdated(
subscription.status === "trialing" &&
plan.freeTrial?.onTrialEnd
) {
await plan.freeTrial.onTrialEnd({ subscription }, ctx);
await plan.freeTrial.onTrialEnd(
{ subscription: subscriptionUpdated },
ctx,
);
}
if (
stripeSubscriptionUpdated.status === "incomplete_expired" &&
subscription.status === "trialing" &&
plan.freeTrial?.onTrialExpired
) {
await plan.freeTrial.onTrialExpired(subscription, ctx);
await plan.freeTrial.onTrialExpired(subscriptionUpdated, ctx);
}
}
} catch (error: any) {
@@ -462,35 +465,43 @@ export async function onSubscriptionDeleted(
trialEnd: new Date(stripeSubscriptionDeleted.trial_end * 1000),
}
: {};
await ctx.context.adapter.update({
model: "subscription",
where: [
{
field: "id",
value: subscription.id,
const subscriptionUpdated =
await ctx.context.adapter.update<Subscription>({
model: "subscription",
where: [
{
field: "id",
value: subscription.id,
},
],
update: {
...trial,
status: "canceled",
updatedAt: new Date(),
cancelAtPeriodEnd: stripeSubscriptionDeleted.cancel_at_period_end,
cancelAt: stripeSubscriptionDeleted.cancel_at
? new Date(stripeSubscriptionDeleted.cancel_at * 1000)
: null,
canceledAt: stripeSubscriptionDeleted.canceled_at
? new Date(stripeSubscriptionDeleted.canceled_at * 1000)
: null,
endedAt: stripeSubscriptionDeleted.ended_at
? new Date(stripeSubscriptionDeleted.ended_at * 1000)
: null,
stripeScheduleId: null,
},
],
update: {
...trial,
status: "canceled",
updatedAt: new Date(),
cancelAtPeriodEnd: stripeSubscriptionDeleted.cancel_at_period_end,
cancelAt: stripeSubscriptionDeleted.cancel_at
? new Date(stripeSubscriptionDeleted.cancel_at * 1000)
: null,
canceledAt: stripeSubscriptionDeleted.canceled_at
? new Date(stripeSubscriptionDeleted.canceled_at * 1000)
: null,
endedAt: stripeSubscriptionDeleted.ended_at
? new Date(stripeSubscriptionDeleted.ended_at * 1000)
: null,
stripeScheduleId: null,
},
});
});
// Practically unreachable. A null here means the row was deleted between the read above and this update.
if (!subscriptionUpdated) {
ctx.context.logger.warn(
`Stripe webhook warning: Subscription ${subscription.id} update returned no row (likely deleted concurrently), skipping callbacks`,
);
return;
}
await options.subscription.onSubscriptionDeleted?.({
event,
stripeSubscription: stripeSubscriptionDeleted,
subscription,
subscription: subscriptionUpdated,
});
} else {
ctx.context.logger.warn(