Files
calendar/packages/features/ee/billing/api/webhook/__handler.ts
T
Omar LópezandGitHub 2bdf65ce8c fix: billing webhook secret (#17005)
* fix: billing webhook secret

* Update __handler.test.ts
2024-10-08 21:26:39 +00:00

64 lines
2.3 KiB
TypeScript

/// <reference types="stripe-event-types" />
import { buffer } from "micro";
import type { NextApiRequest } from "next";
import type Stripe from "stripe";
import stripe from "@calcom/app-store/stripepayment/lib/server";
import { HttpError } from "@calcom/lib/http-error";
/** Stripe Webhook Handler Mappings */
export type SWHMap = {
[T in Stripe.DiscriminatedEvent as T["type"]]: {
[K in keyof T as Exclude<K, "type">]: T[K];
};
};
export type LazyModule<D> = Promise<{
default: (data: D) => unknown | Promise<unknown>;
}>;
type SWHandlers = {
[K in keyof SWHMap]?: () => LazyModule<SWHMap[K]["data"]>;
};
/** Just a shorthand for HttpError */
export class HttpCode extends HttpError {
constructor(statusCode: number, message: string) {
super({ statusCode, message });
}
}
/**
* Allows us to split big API handlers by webhook event, and lazy load them.
* It already handles the Stripe signature verification and event construction.
* @param handlers - A mapping of Stripe webhook event types to lazy loaded handlers.
* @returns A function that can be used as a Next.js API handler.
* @example
* ```ts
* stripeWebhookHandler({
* "payment_intent.succeeded": () => import("./_lazyLoadedSuccessHandler"),
* "customer.subscription.deleted": () => import("./_customer.subscription.deleted"),
* })
* ```
*/
export const stripeWebhookHandler = (handlers: SWHandlers) => async (req: NextApiRequest) => {
const STRIPE_WEBHOOK_SECRET = process.env.STRIPE_WEBHOOK_SECRET_BILLING;
const sig = req.headers["stripe-signature"];
if (!sig) throw new HttpCode(400, "Missing stripe-signature");
if (!STRIPE_WEBHOOK_SECRET) throw new HttpCode(500, "Missing STRIPE_WEBHOOK_SECRET");
const requestBuffer = await buffer(req);
const payload = requestBuffer.toString();
const event = stripe.webhooks.constructEvent(
payload,
sig,
STRIPE_WEBHOOK_SECRET
) as Stripe.DiscriminatedEvent;
const handlerGetter = handlers[event.type];
if (!handlerGetter) throw new HttpCode(202, `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}`);
// @ts-expect-error - we know the handler is defined and accpets the data type
return await handler(event.data);
};