fix(stripe): update subscription webhook handling should use customer id as a fallback

This commit is contained in:
Bereket Engida
2025-03-02 13:36:51 +03:00
parent 25de4b5f4f
commit 1c16655aba
4 changed files with 28 additions and 20 deletions

View File

@@ -20,6 +20,7 @@ import { nextCookies } from "better-auth/next-js";
import { passkey } from "better-auth/plugins/passkey";
import { stripe } from "@better-auth/stripe";
import { Stripe } from "stripe";
import Database from "better-sqlite3";
const from = process.env.BETTER_AUTH_EMAIL || "delivered@resend.dev";
const to = process.env.TEST_EMAIL || "";
@@ -50,10 +51,7 @@ const STARTER_PRICE_ID = {
export const auth = betterAuth({
appName: "Better Auth Demo",
database: {
dialect,
type: process.env.USE_MYSQL ? "mysql" : "sqlite",
},
database: new Database("stripe-1.db"),
emailVerification: {
async sendVerificationEmail({ user, url }) {
const res = await resend.emails.send({

View File

@@ -93,19 +93,32 @@ export async function onSubscriptionUpdated(
const subscriptionUpdated = event.data.object as Stripe.Subscription;
const priceId = subscriptionUpdated.items.data[0].price.id;
const plan = await getPlanByPriceId(options, priceId);
const stripeId = subscriptionUpdated.id;
const subscription = await ctx.context.adapter.findOne<Subscription>({
const referenceId = subscriptionUpdated.metadata?.referenceId;
const subscriptionId = subscriptionUpdated.id;
const customerId = subscriptionUpdated.customer.toString();
let subscription = await ctx.context.adapter.findOne<Subscription>({
model: "subscription",
where: [
{
field: "stripeSubscriptionId",
value: stripeId,
},
],
where: referenceId
? [{ field: "referenceId", value: referenceId }]
: subscriptionId
? [{ field: "stripeSubscriptionId", value: subscriptionId }]
: [],
});
if (!subscription) {
const subs = await ctx.context.adapter.findMany<Subscription>({
model: "subscription",
where: [{ field: "stripeCustomerId", value: customerId }],
});
if (subs.length > 1) {
logger.warn(
`Stripe webhook error: Multiple subscriptions found for customerId: ${customerId} and no referenceId or subscriptionId is provided`,
);
return;
}
subscription = subs[0];
}
const seats = subscriptionUpdated.items.data[0].quantity;
await ctx.context.adapter.update({
model: "subscription",
@@ -125,8 +138,8 @@ export async function onSubscriptionUpdated(
},
where: [
{
field: "stripeSubscriptionId",
value: subscriptionUpdated.id,
field: "referenceId",
value: subscription.referenceId,
},
],
});
@@ -170,7 +183,7 @@ export async function onSubscriptionUpdated(
}
}
} catch (error: any) {
logger.error(`Stripe webhook failed. Error: ${error.message}`);
logger.error(`Stripe webhook failed. Error: ${error}`);
}
}
@@ -217,6 +230,6 @@ export async function onSubscriptionDeleted(
}
}
} catch (error: any) {
logger.error(`Stripe webhook failed. Error: ${error.message}`);
logger.error(`Stripe webhook failed. Error: ${error}`);
}
}

View File

@@ -366,7 +366,6 @@ export const stripe = <O extends StripeOptions>(options: O) => {
},
],
});
console.log({ subscription });
if (
!subscription ||
subscription.cancelAtPeriodEnd ||
@@ -382,7 +381,6 @@ export const stripe = <O extends StripeOptions>(options: O) => {
const currentSubscription = stripeSubscription.data.find(
(sub) => sub.id === subscription.stripeSubscriptionId,
);
console.log({ currentSubscription });
if (currentSubscription?.cancel_at_period_end === true) {
await ctx.context.adapter.update({
model: "subscription",

View File

@@ -592,7 +592,6 @@ describe("stripe", async () => {
mockStripeForEvents.webhooks.constructEvent.mockReturnValue(updateEvent);
await eventTestAuth.handler(updateRequest);
expect(onSubscriptionUpdate).toHaveBeenCalledWith(
expect.objectContaining({
event: expect.any(Object),