import { BackendVariables } from "@Common/config/variables/Variables"; import ApiController from "@Common/system/controller-pattern/ApiController"; import { Controller, Post } from "@ControllerPattern/index"; import SubscriptionsService from "@Services/admin/SubscriptionsService/SubscriptionsService.ts"; import StripeService from "@Services/common/StripeService/StripeService"; import { validateOrReject } from "class-validator"; import { Request, Response } from "express"; import { Subscription } from "le-coffre-resources/dist/Admin"; import { Service } from "typedi"; @Controller() @Service() export default class StripeWebhooks extends ApiController { constructor(private stripeService: StripeService, private subscriptionsService: SubscriptionsService, private backendVariables: BackendVariables) { super(); } /** * @description Create a new checkout session */ @Post("/api/v1/webhooks/stripe") protected async post(req: Request, response: Response) { try { const event = req.body; switch (event.type) { //Manage plan switch + recurring payment case "invoice.payment_succeeded": if (event.data.object.billing_reason !== "subscription_update" && event.data.object.billing_reason !== "subscription_cycle") break; const stripeSubscription = await this.stripeService.getClient().subscriptions.retrieve(event.data.object.subscription); if(stripeSubscription.metadata['env'] !== this.backendVariables.ENV) break; const existingSubscription = await this.subscriptionsService.get({where : {stripe_subscription_id : stripeSubscription.id}}); if(!existingSubscription[0]) break; const subscriptionUpdate: any = {}; subscriptionUpdate.start_date = new Date(stripeSubscription.current_period_start * 1000); subscriptionUpdate.end_date = new Date(stripeSubscription.current_period_end * 1000); subscriptionUpdate.nb_seats = stripeSubscription.items.data[0]?.quantity; if(stripeSubscription.items.data[0]?.price?.id === this.backendVariables.STRIPE_STANDARD_SUBSCRIPTION_PRICE_ID || stripeSubscription.items.data[0]?.price?.id === this.backendVariables.STRIPE_STANDARD_ANNUAL_SUBSCRIPTION_PRICE_ID){ subscriptionUpdate.type = "STANDARD"; } else{ subscriptionUpdate.type = "UNLIMITED"; } const subscriptionEntityUpdate = Subscription.hydrate(subscriptionUpdate); await validateOrReject(subscriptionEntityUpdate, { groups: ["updateSubscription"], forbidUnknownValues: false }); await this.subscriptionsService.update(existingSubscription[0].uid ,subscriptionEntityUpdate); //Manage subscription creation and first payment case "checkout.session.completed": if (event.data.object.status !== "complete") break; const subscription = JSON.parse(event.data.object.metadata.subscription); const env = event.data.object.metadata.env; if (env !== this.backendVariables.ENV) break; subscription.stripe_subscription_id = event.data.object.subscription; await this.stripeService.getClient().subscriptions.update(subscription.stripe_subscription_id, { metadata: { env: env, }, } ) const subscriptionInfo = await this.stripeService .getClient() .subscriptions.retrieve(subscription.stripe_subscription_id); subscription.start_date = new Date(subscriptionInfo.current_period_start * 1000); subscription.end_date = new Date(subscriptionInfo.current_period_end * 1000); const subscriptionEntity = Subscription.hydrate(subscription); await validateOrReject(subscriptionEntity, { groups: ["createSubscription"], forbidUnknownValues: false }); await this.subscriptionsService.create(subscriptionEntity); break; //Manage plan expiration case "customer.subscription.deleted": const currentSubscription = await this.stripeService.getClient().subscriptions.retrieve(event.data.object.id); if(currentSubscription.metadata['env'] !== this.backendVariables.ENV) break; const subscriptionToDelete = await this.subscriptionsService.get({where : {stripe_subscription_id : event.data.object.id}}); if(!subscriptionToDelete[0]) break; await this.subscriptionsService.delete(subscriptionToDelete[0].uid); default: break; } response.json({ received: true }); } catch (error) { this.httpInternalError(response, error); return; } } }