(event: Stripe.Event)
| 980 | * Note: Enterprise plans no longer have overages |
| 981 | */ |
| 982 | export async function handleInvoiceFinalized(event: Stripe.Event) { |
| 983 | try { |
| 984 | const invoice = event.data.object as Stripe.Invoice |
| 985 | const subscription = invoice.parent?.subscription_details?.subscription |
| 986 | const stripeSubscriptionId = typeof subscription === 'string' ? subscription : subscription?.id |
| 987 | if (!stripeSubscriptionId) { |
| 988 | logger.info('No subscription found on invoice; skipping finalized handler', { |
| 989 | invoiceId: invoice.id, |
| 990 | }) |
| 991 | return |
| 992 | } |
| 993 | if (invoice.billing_reason && invoice.billing_reason !== 'subscription_cycle') return |
| 994 | |
| 995 | const records = await db |
| 996 | .select() |
| 997 | .from(subscriptionTable) |
| 998 | .where(eq(subscriptionTable.stripeSubscriptionId, stripeSubscriptionId)) |
| 999 | .limit(1) |
| 1000 | |
| 1001 | if (records.length === 0) return |
| 1002 | const sub = records[0] |
| 1003 | |
| 1004 | const invoicePeriod = getSubscriptionLinePeriod(invoice, stripeSubscriptionId) |
| 1005 | if (!invoicePeriod) { |
| 1006 | logger.error('Missing subscription line period on subscription cycle invoice', { |
| 1007 | invoiceId: invoice.id, |
| 1008 | stripeSubscriptionId, |
| 1009 | }) |
| 1010 | if (isEnterprise(sub.plan)) { |
| 1011 | await resetUsageForSubscription({ plan: sub.plan, referenceId: sub.referenceId }) |
| 1012 | } |
| 1013 | return |
| 1014 | } |
| 1015 | |
| 1016 | if (isEnterprise(sub.plan)) { |
| 1017 | await resetUsageForSubscription({ |
| 1018 | plan: sub.plan, |
| 1019 | referenceId: sub.referenceId, |
| 1020 | periodStart: invoicePeriod.periodStart, |
| 1021 | periodEnd: invoicePeriod.periodEnd, |
| 1022 | }) |
| 1023 | return |
| 1024 | } |
| 1025 | |
| 1026 | await stripeWebhookIdempotency.executeWithIdempotency( |
| 1027 | 'invoice-finalized', |
| 1028 | event.id, |
| 1029 | async () => { |
| 1030 | const stripe = requireStripeClient() |
| 1031 | const periodStart = Math.floor(invoicePeriod.periodStart.getTime() / 1000) |
| 1032 | const periodEnd = Math.floor(invoicePeriod.periodEnd.getTime() / 1000) |
| 1033 | const billingPeriod = new Date(periodEnd * 1000).toISOString().slice(0, 7) |
| 1034 | |
| 1035 | const totalOverage = await calculateSubscriptionOverage({ |
| 1036 | ...sub, |
| 1037 | periodStart: new Date(periodStart * 1000), |
| 1038 | periodEnd: new Date(periodEnd * 1000), |
| 1039 | }) |
no test coverage detected