diff --git a/packages/features/ee/billing/api/webhook/_customer.subscription.deleted.ts b/packages/features/ee/billing/api/webhook/_customer.subscription.deleted.ts index 383c3a144f..0e3c5bdd2b 100644 --- a/packages/features/ee/billing/api/webhook/_customer.subscription.deleted.ts +++ b/packages/features/ee/billing/api/webhook/_customer.subscription.deleted.ts @@ -9,10 +9,25 @@ const STRIPE_TEAM_PRODUCT_ID = process.env.STRIPE_TEAM_PRODUCT_ID || ""; const stripeWebhookProductHandler = (handlers: Handlers) => async (data: Data) => { const subscription = data.object; - // @ts-expect-error - we know subscription.plan.product is defined when unsubscribing - const productId = subscription.plan.product; // prod_xxxxx - - const handlerGetter = handlers[productId]; + let productId: string | null = null; + // @ts-expect-error - support legacy just in case. + if (subscription.plan) { + // @ts-expect-error - we know subscription.plan.product is defined when unsubscribing + productId = subscription.plan.product; // prod_xxxxx + } else { + const subscriptionItem = subscription.items?.data?.[0]; + if (!subscriptionItem) { + throw new Error("Subscription item and plan missing"); + } + const product = subscription.items.data[0]?.plan.product; + if (product) { + productId = typeof product === "string" ? product : product.id; + } + } + if (typeof productId !== "string") { + throw new Error(`Unable to determine Product ID from subscription: ${subscription.id}`); + } + const handlerGetter = handlers[productId as any]; if (!handlerGetter) throw new HttpCode(202, `No product handler found for product: ${productId}`); const handler = (await handlerGetter())?.default; // auto catch unsupported Stripe products.