A11yWatch/a11ywatch-core

View on GitHub
src/web/routes_groups/stripe.ts

Summary

Maintainability
B
5 hrs
Test Coverage
import type { FastifyInstance } from "fastify";
import { config } from "../../config";
import { stripe } from "../../core/external";
import { UsersController, WebsitesController } from "../../core/controllers";
import Stripe from "stripe";
import { addMonths } from "date-fns";

// Data to use from stripe web hook [todo: types may be available on newer versions]
type StripeData = {
  object: { customer_email: string; email?: string; billing_reason: string };
};

export const stripeHook = async (req, res) => {
  let event: Stripe.Event;

  try {
    event = await stripe.webhooks.constructEventAsync(
      req.body.raw,
      req.headers["stripe-signature"],
      config.STRIPE_WH_SECRET
    );
  } catch (err) {
    res.status(400).send(`Webhook Error: ${err.message}`);
    return;
  }

  let statusCode = 200;
  // todo: bind to stripe api version
  const { object: stripeCustomer } = (event?.data as StripeData) ?? {};

  const userEmail = stripeCustomer?.customer_email ?? stripeCustomer?.email;

  switch (event.type) {
    case "customer.subscription.deleted": {
      const [user, collection] = await UsersController().getUser({
        email: userEmail,
      });

      await WebsitesController().removeWebsite({
        userId: user.id,
        deleteMany: true,
      });

      await UsersController().downgradeStripeUserValues({
        user,
        collection,
      });
      break;
    }
    case "invoice.payment_succeeded": {
      if (
        ["manual", "subscription_cycle", "subscription_update"].includes(
          stripeCustomer.billing_reason
        )
      ) {
        const [user, collection] = await UsersController().getUser({
          email: userEmail,
        });

        if (user?.paymentSubscription?.status !== "active") {
          user.paymentSubscription.status = "active";
        }

        await collection.updateOne(
          { email: user.email },
          {
            $set:
              stripeCustomer.billing_reason === "subscription_cycle"
                ? {
                    paymentSubscription: user.paymentSubscription,
                    usageAnchorDate: user.usageAnchorDate
                      ? addMonths(user.usageAnchorDate, 1)
                      : new Date(),
                  }
                : {
                    paymentSubscription: user.paymentSubscription,
                  },
          }
        );
      }
      break;
    }

    case "customer.created": {
      const [user, collection] = await UsersController().getUser({
        email: userEmail,
      });

      await collection.updateOne(
        { email: user.email },
        {
          $set: {
            usageAnchorDate: new Date(),
          },
        }
      );

      break;
    }

    default: {
      statusCode = 200;
    }
  }

  res.status(statusCode).send(true);
};

// stripe web hook routes
export const setStripeRoutes = (app: FastifyInstance) => {
  app.post("/api/stripes/event", stripeHook);
};