Files
calendar/packages/features/ee/billing/api/webhook/_invoice.payment_failed.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

43 lines
1.5 KiB
TypeScript

import { getBillingProviderService, getSeatBillingStrategyFactory } from "@calcom/ee/billing/di/containers/Billing";
import logger from "@calcom/lib/logger";
import type { SWHMap } from "./__handler";
const log = logger.getSubLogger({ prefix: ["invoice-payment-failed"] });
type Data = SWHMap["invoice.payment_failed"]["data"];
const handler = async (data: Data) => {
const invoice = data.object;
const subscriptionId =
typeof invoice.subscription === "string" ? invoice.subscription : invoice.subscription?.id;
if (!subscriptionId) {
log.debug("Not a subscription invoice, skipping");
return { success: true, message: "not a subscription invoice" };
}
let failureReason = invoice.status ?? "payment_failed";
const paymentIntentId =
typeof invoice.payment_intent === "string" ? invoice.payment_intent : invoice.payment_intent?.id;
if (paymentIntentId) {
const billingProviderService = getBillingProviderService();
const paymentFailureReason = await billingProviderService.getPaymentIntentFailureReason(paymentIntentId);
failureReason = paymentFailureReason ?? failureReason;
}
const factory = getSeatBillingStrategyFactory();
const strategy = await factory.createBySubscriptionId(subscriptionId);
const { handled } = await strategy.onPaymentFailed({ lines: invoice.lines }, failureReason);
if (handled) {
log.info("Strategy handled payment failure", { subscriptionId, failureReason });
}
return { success: true, handled };
};
export default handler;