fix: Stop throwing 202 for stripe handler (#20397)

* dont throw - log error

* invoice paid

* update to warn

* Update packages/features/ee/billing/api/webhook/_invoice.paid.ts

---------

Co-authored-by: Keith Williams <keithwillcode@gmail.com>
This commit is contained in:
sean-brydon
2025-03-27 15:06:15 +00:00
committed by GitHub
co-authored by Keith Williams
parent 2aa583bb6d
commit da47168941
4 changed files with 41 additions and 16 deletions
@@ -53,7 +53,7 @@ describe("stripeWebhookHandler", () => {
await expect(handler(req)).rejects.toThrow(new HttpCode(500, "Missing STRIPE_WEBHOOK_SECRET"));
});
it("should throw an error if event type is unhandled", async () => {
it("should return success false if event type is unhandled", async () => {
const { req, res } = createMocks<CustomNextApiRequest, CustomNextApiResponse>({
method: "POST",
headers: {
@@ -65,9 +65,11 @@ describe("stripeWebhookHandler", () => {
(stripe.webhooks.constructEvent as any).mockReturnValueOnce({ type: "unhandled_event" });
const handler = stripeWebhookHandler({});
await expect(handler(req)).rejects.toThrow(
new HttpCode(202, "Unhandled Stripe Webhook event type unhandled_event")
);
const response = await handler(req);
expect(response).toEqual({
success: false,
message: "Unhandled Stripe Webhook event type unhandled_event",
});
});
it("should call the appropriate handler for a valid event", async () => {
@@ -54,10 +54,22 @@ export const stripeWebhookHandler = (handlers: SWHandlers) => async (req: NextAp
STRIPE_WEBHOOK_SECRET
) as Stripe.DiscriminatedEvent;
const handlerGetter = handlers[event.type];
if (!handlerGetter) throw new HttpCode(202, `Unhandled Stripe Webhook event type ${event.type}`);
if (!handlerGetter) {
console.log("Unhandled Stripe Webhook event type", event.type);
return {
success: false,
message: `Unhandled Stripe Webhook event type ${event.type}`,
};
}
const handler = (await handlerGetter())?.default;
// auto catch unsupported Stripe events.
if (!handler) throw new HttpCode(202, `Unhandled Stripe Webhook event type ${event.type}`);
if (!handler) {
console.log("Unhandled Stripe Webhook event type", event.type);
return {
success: false,
message: `Unhandled Stripe Webhook event type ${event.type}`,
};
}
// @ts-expect-error - we know the handler is defined and accepts the data type
return await handler(event.data);
};
@@ -1,4 +1,3 @@
import { HttpCode } from "./__handler";
import type { LazyModule, SWHMap } from "./__handler";
type Data = SWHMap["customer.subscription.deleted"]["data"];
@@ -28,10 +27,22 @@ const stripeWebhookProductHandler = (handlers: Handlers) => async (data: Data) =
throw new Error(`Unable to determine Product ID from subscription: ${subscription.id}`);
}
const handlerGetter = handlers[productId as any];
if (!handlerGetter) throw new HttpCode(202, `No product handler found for product: ${productId}`);
if (!handlerGetter) {
console.log("No product handler found for product", productId);
return {
success: false,
message: `No product handler found for product: ${productId}`,
};
}
const handler = (await handlerGetter())?.default;
// auto catch unsupported Stripe products.
if (!handler) throw new HttpCode(202, `No product handler found for product: ${productId}`);
if (!handler) {
console.log("No product handler found for product", productId);
return {
success: false,
message: `No product handler found for product: ${productId}`,
};
}
return await handler(data);
};
@@ -16,7 +16,7 @@ const stripeWebhookProductHandler = (handlers: Handlers) => async (data: Data) =
// Only handle subscription invoices
if (!invoice.subscription) {
log.warn("Not a subscription invoice, skipping");
return { success: true };
return { success: false, message: "Not a subscription invoice, skipping" };
}
// Get the product ID from the first subscription item
@@ -24,8 +24,8 @@ const stripeWebhookProductHandler = (handlers: Handlers) => async (data: Data) =
const productId = firstItem?.price?.product as string; // prod_xxxxx
if (!productId) {
log.error("No product ID found in invoice, skipping");
return { success: true };
log.warn("No product ID found in invoice, skipping");
return { success: false, message: "No product ID found in invoice, skipping" };
}
const handlerGetter = handlers[productId as keyof typeof handlers];
@@ -34,14 +34,14 @@ const stripeWebhookProductHandler = (handlers: Handlers) => async (data: Data) =
* If no handler is found, we skip the product. A handler could be null if we don't need webhooks to handle the business logic for the product.
*/
if (!handlerGetter) {
log.error(`Skipping product: ${productId} because no handler found`);
return { success: true };
log.warn(`Skipping product: ${productId} because no handler found`);
return { success: false, message: `Skipping product: ${productId} because no handler found` };
}
const handler = (await handlerGetter())?.default;
// auto catch unsupported Stripe products.
if (!handler) {
log.error(`Skipping product: ${productId} because no handler found`);
return { success: true };
log.warn(`Skipping product: ${productId} because no handler found`);
return { success: false, message: `Skipping product: ${productId} because no handler found` };
}
return await handler(data);
};