Files
calendar/packages/features/ee/billing/api/webhook/_invoice.paid.team.ts
T
sean-brydonGitHubunknown <>Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
f4abbb2de1 feat: refactor billing to strategy implemention (#27828)
* factory and statergie

* chore: use correct method of DI

* feat: add onchagne

* add logic to HWM stat

* add webhook resolver methods to each statergy

* move seat tracking + webhooks over to own statergy

* Move to factory base approach

* move logic to correct class

* rename create -> createByTeamId

* fix: remove debug `true ||` overrides from IS_STRIPE_ENABLED and IS_TEAM_BILLING_ENABLED

Remove accidentally committed debug overrides that short-circuited
IS_STRIPE_ENABLED and IS_TEAM_BILLING_ENABLED to always be true,
bypassing Stripe credential checks. This would break self-hosted
instances without Stripe configured.

Identified by cubic (https://cubic.dev)

Co-Authored-By: unknown <>

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-02-10 13:11:36 -03:00

56 lines
1.7 KiB
TypeScript

import { getSeatBillingStrategyFactory } from "@calcom/features/ee/billing/di/containers/Billing";
import logger from "@calcom/lib/logger";
import { z } from "zod";
import type { SWHMap } from "./__handler";
const log = logger.getSubLogger({ prefix: ["invoice-paid-team"] });
const invoicePaidSchema = z.object({
object: z.object({
customer: z.string(),
subscription: z.string(),
billing_reason: z.string().nullable(),
lines: z.object({
data: z.array(
z.object({
subscription_item: z.string(),
period: z
.object({
start: z.number(),
end: z.number(),
})
.optional(),
})
),
}),
}),
});
const handler = async (data: SWHMap["invoice.paid"]["data"]) => {
const { object: invoice } = invoicePaidSchema.parse(data);
const subscriptionId = invoice.subscription;
log.debug(`Processing invoice paid webhook for team subscription ${subscriptionId}`, {
billingReason: invoice.billing_reason,
customerId: invoice.customer,
});
if (invoice.billing_reason === "subscription_cycle") {
const periodStart = invoice.lines.data[0]?.period?.start;
if (!periodStart) {
log.warn(`Invoice has no period start for subscription ${subscriptionId}, skipping renewal handling`);
return { success: true };
}
log.info(`Processing renewal invoice for team subscription ${subscriptionId}`);
const factory = getSeatBillingStrategyFactory();
const strategy = await factory.createBySubscriptionId(subscriptionId);
await strategy.onRenewalPaid(subscriptionId, new Date(periodStart * 1000));
}
return { success: true };
};
export default handler;