@@ -26,9 +26,18 @@ import {
2626import { getStripeCustomerId } from '@/lib/stripe-utils'
2727import { logger } from '@/util/logger'
2828
29+ /**
30+ * Extracts a string ID from a Stripe object that may be a string or an
31+ * expanded object with an `id` field.
32+ */
33+ function getStripeId ( obj : string | { id : string } ) : string {
34+ return typeof obj === 'string' ? obj : obj . id
35+ }
36+
2937/**
3038 * Checks whether a Stripe subscription ID belongs to an organization.
31- * Used to guard user-subscription handlers from processing org subscriptions.
39+ * Used to guard user-subscription handlers from processing org subscriptions
40+ * on invoice events (where subscription metadata isn't directly available).
3241 */
3342async function isOrgSubscription ( subscriptionId : string ) : Promise < boolean > {
3443 const orgs = await db
@@ -239,6 +248,7 @@ async function handleCheckoutSessionCompleted(
239248
240249async function handleSubscriptionEvent ( subscription : Stripe . Subscription ) {
241250 const organizationId = subscription . metadata ?. organization_id
251+ if ( ! organizationId ) return
242252
243253 logger . info (
244254 {
@@ -247,17 +257,9 @@ async function handleSubscriptionEvent(subscription: Stripe.Subscription) {
247257 customerId : subscription . customer ,
248258 organizationId,
249259 } ,
250- 'Subscription event received' ,
260+ 'Organization subscription event received' ,
251261 )
252262
253- if ( ! organizationId ) {
254- logger . debug (
255- { subscriptionId : subscription . id } ,
256- 'Subscription event received without organization_id in metadata (user subscription)' ,
257- )
258- return
259- }
260-
261263 try {
262264 // Handle subscription cancellation
263265 if ( subscription . status === 'canceled' ) {
@@ -373,18 +375,18 @@ const webhookHandler = async (req: NextRequest): Promise<NextResponse> => {
373375 case 'customer.subscription.created' :
374376 case 'customer.subscription.updated' : {
375377 const sub = event . data . object as Stripe . Subscription
376- // Handle org subscriptions (legacy)
377- await handleSubscriptionEvent ( sub )
378- // Handle user subscriptions (new) — skip org subscriptions
379- if ( ! sub . metadata ?. organization_id ) {
378+ if ( sub . metadata ?. organization_id ) {
379+ await handleSubscriptionEvent ( sub )
380+ } else {
380381 await handleSubscriptionUpdated ( { stripeSubscription : sub , logger } )
381382 }
382383 break
383384 }
384385 case 'customer.subscription.deleted' : {
385386 const sub = event . data . object as Stripe . Subscription
386- await handleSubscriptionEvent ( sub )
387- if ( ! sub . metadata ?. organization_id ) {
387+ if ( sub . metadata ?. organization_id ) {
388+ await handleSubscriptionEvent ( sub )
389+ } else {
388390 await handleSubscriptionDeleted ( { stripeSubscription : sub , logger } )
389391 }
390392 break
@@ -543,12 +545,8 @@ const webhookHandler = async (req: NextRequest): Promise<NextResponse> => {
543545 case 'invoice.paid' : {
544546 const invoice = event . data . object as Stripe . Invoice
545547 await handleInvoicePaid ( invoice )
546- // Handle subscription invoice payments (user subscriptions only)
547548 if ( invoice . subscription ) {
548- const subId =
549- typeof invoice . subscription === 'string'
550- ? invoice . subscription
551- : invoice . subscription . id
549+ const subId = getStripeId ( invoice . subscription )
552550 if ( ! ( await isOrgSubscription ( subId ) ) ) {
553551 await handleSubscriptionInvoicePaid ( { invoice, logger } )
554552 }
@@ -557,12 +555,8 @@ const webhookHandler = async (req: NextRequest): Promise<NextResponse> => {
557555 }
558556 case 'invoice.payment_failed' : {
559557 const invoice = event . data . object as Stripe . Invoice
560- // Handle subscription payment failures (user subscriptions only)
561558 if ( invoice . subscription ) {
562- const subId =
563- typeof invoice . subscription === 'string'
564- ? invoice . subscription
565- : invoice . subscription . id
559+ const subId = getStripeId ( invoice . subscription )
566560 if ( ! ( await isOrgSubscription ( subId ) ) ) {
567561 await handleSubscriptionInvoicePaymentFailed ( { invoice, logger } )
568562 }
0 commit comments