Hey all, I’m running into a race condition where S...
# orm-help
c
Hey all, I’m running into a race condition where Stripe sends two webhooks: “invoice.paid” and “checkout.session.completed” (which are sent in random order) and my backend creates a “Payment” record when I receive the “invoice.paid” event, and a “Customer” record when I receive the “checkout.session.completed” event. The issue comes to play when I need the “customerId” for the “Payment” record. On both event handlers, I “upsert” a “Customer” record to ensure that a “customerId” is always present when creating a “Payment” record; however, two “Customer” records end up getting created because it doesn’t seem like Prisma ORM “locks” the table from reads/writes. Has anyone had an issue like this before? Will post code in the thread.
Copy code
<http://webhookRouter.post|webhookRouter.post>('/webhook', async (req: Request, res: Response) => {
  const data = req.body.data;
  const eventType = req.body.type;

  switch (eventType) {
    case 'checkout.session.completed': {
      const stripeCustomer = await stripeClient.customers.retrieve(data.object.customer);

      if (stripeCustomer.deleted) {
        break;
      }

      const customerId = (await prismaClient.customer.findFirst({
        where: {
          stripeCustomerId: stripeCustomer.id
        }
      }))?.id;

      const customerData = {
        companyName: data.object.metadata.companyName,
        primaryEmail: stripeCustomer.email,
        primaryPhone: stripeCustomer.phone,
        mondayAccountName: data.object.metadata.accountName,
        stripeCustomerId: stripeCustomer.id
      };

      const customer = await prismaClient.customer.upsert({
        where: {
          // We default to -1 because we know this id doesn't exist in the DB
          // and Prisma's upsert method, although it says it accepts number | undefined,
          // will throw an error if the id resolves to undefined.
          id: customerId || -1
        },
        create: customerData,
        update: customerData
      });

      console.log(`Created Customer with ID: ${customer.id}`);

      break;
    }
    case 'invoice.paid': {
      let customer = await prismaClient.customer.findFirst({
        where: {
          stripeCustomerId: data.object.customer
        }
      });

      console.log(customer);

      if (!customer) {
        customer = await prismaClient.customer.create({
          data: {
            companyName: '',
            stripeCustomerId: data.object.customer
          }
        });
      }

      const payment = await prismaClient.payment.create({
        data: {
          amountDue: data.object.amount_due / 100,
          amountPaid: data.object.amount_paid / 100,
          paymentDate: new Date(data.object.created * 1000),
          customerId: customer.id,
          stripeCustomerId: customer.stripeCustomerId,
          stripeInvoiceId: data.object.id,
          stripeSubscriptionId: data.object.subscription,
          stripeProductId: data.object.lines.data[0].price.product
        }
      });

      console.log(`Created Payment with ID: ${payment.id}`);

      break;
    } 
    case 'invoice.payment_failed':
      break;
    default:
      break;
  }

  res.sendStatus(200);
});