Files
2025-12-09 10:56:14 -05:00

144 lines
5.4 KiB
TypeScript

/*
* This file is part of a proprietary work.
*
* Copyright (c) 2025 Fossorial, Inc.
* All rights reserved.
*
* This file is licensed under the Fossorial Commercial License.
* You may not use this file except in compliance with the License.
* Unauthorized use, copying, modification, or distribution is strictly prohibited.
*
* This file is not licensed under the AGPLv3.
*/
import stripe from "#private/lib/stripe";
import privateConfig from "#private/lib/config";
import logger from "@server/logger";
import createHttpError from "http-errors";
import { response } from "@server/lib/response";
import { Request, Response, NextFunction } from "express";
import HttpCode from "@server/types/HttpCode";
import Stripe from "stripe";
import { handleCustomerCreated } from "./hooks/handleCustomerCreated";
import { handleSubscriptionCreated } from "./hooks/handleSubscriptionCreated";
import { handleSubscriptionUpdated } from "./hooks/handleSubscriptionUpdated";
import { handleCustomerUpdated } from "./hooks/handleCustomerUpdated";
import { handleSubscriptionDeleted } from "./hooks/handleSubscriptionDeleted";
import { handleCustomerDeleted } from "./hooks/handleCustomerDeleted";
export async function billingWebhookHandler(
req: Request,
res: Response,
next: NextFunction
): Promise<any> {
let event: Stripe.Event = req.body;
const endpointSecret =
privateConfig.getRawPrivateConfig().stripe?.webhook_secret;
if (!endpointSecret) {
logger.warn(
"Stripe webhook secret is not configured. Webhook events will not be priocessed."
);
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, ""));
}
// Only verify the event if you have an endpoint secret defined.
// Otherwise use the basic event deserialized with JSON.parse
if (endpointSecret) {
// Get the signature sent by Stripe
const signature = req.headers["stripe-signature"];
if (!signature) {
logger.info("No stripe signature found in headers.");
return next(
createHttpError(
HttpCode.BAD_REQUEST,
"No stripe signature found in headers"
)
);
}
try {
event = stripe!.webhooks.constructEvent(
req.body,
signature,
endpointSecret
);
} catch (err) {
logger.error(`Webhook signature verification failed.`, err);
return next(
createHttpError(
HttpCode.UNAUTHORIZED,
"Webhook signature verification failed"
)
);
}
}
let subscription;
let previousAttributes;
// Handle the event
switch (event.type) {
case "customer.created":
const customer = event.data.object;
logger.info("Customer created: ", customer);
handleCustomerCreated(customer);
break;
case "customer.updated":
const customerUpdated = event.data.object;
logger.info("Customer updated: ", customerUpdated);
handleCustomerUpdated(customerUpdated);
break;
case "customer.deleted":
const customerDeleted = event.data.object;
logger.info("Customer deleted: ", customerDeleted);
handleCustomerDeleted(customerDeleted);
break;
case "customer.subscription.paused":
subscription = event.data.object;
previousAttributes = event.data.previous_attributes;
handleSubscriptionUpdated(subscription, previousAttributes);
break;
case "customer.subscription.resumed":
subscription = event.data.object;
previousAttributes = event.data.previous_attributes;
handleSubscriptionUpdated(subscription, previousAttributes);
break;
case "customer.subscription.deleted":
subscription = event.data.object;
handleSubscriptionDeleted(subscription);
break;
case "customer.subscription.created":
subscription = event.data.object;
handleSubscriptionCreated(subscription);
break;
case "customer.subscription.updated":
subscription = event.data.object;
previousAttributes = event.data.previous_attributes;
handleSubscriptionUpdated(subscription, previousAttributes);
break;
case "customer.subscription.trial_will_end":
subscription = event.data.object;
// Then define and call a method to handle the subscription trial ending.
// handleSubscriptionTrialEnding(subscription);
break;
case "entitlements.active_entitlement_summary.updated":
subscription = event.data.object;
logger.info(
`Active entitlement summary updated for ${subscription}.`
);
// Then define and call a method to handle active entitlement summary updated
// handleEntitlementUpdated(subscription);
break;
default:
// Unexpected event type
logger.info(`Unhandled event type ${event.type}.`);
}
// Return a 200 response to acknowledge receipt of the event
return response(res, {
data: null,
success: true,
error: false,
message: "Webhook event processed successfully",
status: HttpCode.CREATED
});
}