From 71c9567169f1c5fa4a40cd64b18523d0348fe459 Mon Sep 17 00:00:00 2001 From: Alex van Andel Date: Fri, 12 Dec 2025 13:48:06 +0000 Subject: [PATCH] fix: Lazy loaded Stripe proxy to avoid crash on BillingService instantiation (#25400) --- .../ee/billing/di/modules/StripeClient.ts | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/packages/features/ee/billing/di/modules/StripeClient.ts b/packages/features/ee/billing/di/modules/StripeClient.ts index 41a70d454a..07592178d8 100644 --- a/packages/features/ee/billing/di/modules/StripeClient.ts +++ b/packages/features/ee/billing/di/modules/StripeClient.ts @@ -6,9 +6,33 @@ import { DI_TOKENS } from "../tokens"; export const stripeClientModule = createModule(); const token = DI_TOKENS.STRIPE_CLIENT; + +/** + * Creates a deep proxy that only throws when a Stripe function is invoked. + * Property access (e.g. stripe.customers, stripe.customers.create) returns another proxy so that + * code doing capability checks (typeof stripe.customers?.create === "function") does not immediately throw. + */ +function createLazyThrowingStripeStub(error: Error): Stripe { + // Using unknown instead of any to satisfy lint while still allowing broad proxying. + function makeDeepProxy(): unknown { + return new Proxy(function () {}, { + get() { + // Return another proxy for any nested property (resources/methods) + return makeDeepProxy(); + }, + apply() { + throw error; // Throw only when a function is actually called + }, + }); + } + return makeDeepProxy() as Stripe; +} + stripeClientModule.bind(token).toFactory(() => { if (!process.env.STRIPE_PRIVATE_KEY) { - throw new Error("STRIPE_PRIVATE_KEY is not set"); + const error = new Error("STRIPE_PRIVATE_KEY is not set"); + // Inject a stub that defers throwing until a method invocation + return createLazyThrowingStripeStub(error); } return new Stripe(process.env.STRIPE_PRIVATE_KEY!, {