From 944be6d876b1390cae9c92b80fb3a5f87c5b5e21 Mon Sep 17 00:00:00 2001 From: Eric Date: Fri, 18 Aug 2023 18:16:03 -0500 Subject: [PATCH] Website: Update error handling in Stripe webhook. (#13369) Closes: https://github.com/fleetdm/confidential/issues/3356 Changes: - Updated the `receive-from-stripe` webhook not to throw an error if it receives an event that stripe sends before we create a subscription record in the website's database. It will now check if the Stripe customer referenced in the event matches a User record in the database and throws an error if it no matching user is found. --- .../webhooks/receive-from-stripe.js | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/website/api/controllers/webhooks/receive-from-stripe.js b/website/api/controllers/webhooks/receive-from-stripe.js index 966ca5306d..4b8a9e1138 100644 --- a/website/api/controllers/webhooks/receive-from-stripe.js +++ b/website/api/controllers/webhooks/receive-from-stripe.js @@ -42,6 +42,8 @@ module.exports = { fn: async function ({id, type, data, webhookSecret}) { + let assert = require('assert'); + if(!this.req.get('stripe-signature')) { throw 'missingStripeHeader'; } @@ -60,13 +62,32 @@ module.exports = { if(!stripeEventData.subscription) { return; } + assert(stripeEventData.customer !== undefined); // Find the subscription record for this event. let subscriptionIdToFind = stripeEventData.subscription; let subscriptionForThisEvent = await Subscription.findOne({stripeSubscriptionId: subscriptionIdToFind}).populate('user'); + let STRIPE_EVENTS_SENT_BEFORE_A_SUBSCRIPTION_RECORD_EXISTS = [ + 'invoice.created', + 'invoice.finalized', + 'invoice.paid', + 'invoice.payment_succeeded', + ]; + + // If this event is for a subscription that was just created, we won't have a matching Subscription record in the database. This is because we wait until the subscription's invoice is paid to create the record in our database. + // To handle cases like this, we'll check to see if a User with the provided stripe customer ID exists, and throw an error if it does not exist. if(!subscriptionForThisEvent) { - throw new Error(`The Stripe subscription events webhook received a event for a subscription with stripeSubscriptionId: ${subscriptionIdToFind}, but no matching record was found in our database.`); + if(!_.contains(STRIPE_EVENTS_SENT_BEFORE_A_SUBSCRIPTION_RECORD_EXISTS, type)) { + throw new Error(`The Stripe subscription events webhook received a event for a subscription with stripeSubscriptionId: ${subscriptionIdToFind}, but no matching record was found in our database.`); + } else { + let userReferencedInStripeEvent = await User.findOne({stripeCustomerId: stripeEventData.customer}); + if(!userReferencedInStripeEvent){ + throw new Error(`The receive-from-stripe webhook received an event for an invoice (type: ${type}) for a subscription (stripeSubscriptionId: ${subscriptionIdToFind}) but no matching Subscription or User record (stripeCustomerId: ${stripeEventData.customer}) was found in our databse.`); + } else { + return; + } + } } let userForThisSubscription = subscriptionForThisEvent.user;