Handling Stripe webhook errors with Netlify Serverless Functions and SANITY
9 replies
Last updated: Feb 11, 2022
E
Heyo!
Working with a serverless function and Stripe. So after a successful purchase Stripe hits a Netlify Serverless Function which then in turn goes out and hits SANITY and updates the purchased product in the backend.
I am sometimes seeing timeout errors and
Do I need to wrap the
Here is what the full code looks like
Working with a serverless function and Stripe. So after a successful purchase Stripe hits a Netlify Serverless Function which then in turn goes out and hits SANITY and updates the purchased product in the backend.
I am sometimes seeing timeout errors and
Client network socket disconnected before secure TLS connection was established
clientin an async/await statement? Or any other measures I can take to better handle this?
Here is what the full code looks like
import Stripe from "stripe" import { Handler } from "@netlify/functions" import sanityClient from "@sanity/client" // Initialize Stripe client const stripe = new Stripe(process.env.STRIPE_SECRET_KEY, { apiVersion: "2020-08-27", }) // Initialize SANITY client const client = sanityClient({ projectId: process.env.GATSBY_SANITY_ID, dataset: process.env.GATSBY_SANITY_DATASET, token: process.env.SANITY_TOKEN, useCdn: false, // `false` if you want to ensure fresh data apiVersion: "2022-02-01", }) const handler: Handler = async ({ body, headers }) => { try { // check the webhook to make sure it's valid const stripeEvent = stripe.webhooks.constructEvent( body, headers["stripe-signature"], process.env.STRIPE_WEBHOOK_SECRET ) // Only do stuff if this is a successful Stripe Checkout purchase if (stripeEvent.type === "checkout.session.completed") { // Get the session from the webhook and then // Got Stripe to get all of the session data // We need the full line items from the completed purchase const eventObject: any = stripeEvent.data.object const sessionId: string = eventObject.id const sessionData: any = await stripe.checkout.sessions.retrieve( sessionId, { expand: ["line_items", "line_items.data.price.product"], } ) const lineItems: any = sessionData.line_items.data // Loop over each line item and update the stock in SANITY if necessary lineItems.forEach((item) => { const sanityId = item.price.product.metadata.sanityId client .patch(sanityId) // Document ID to patch .dec({ stock: item.quantity }) // Increment field by count .commit() // Perform the patch and return a promise .catch((err) => { console.error("Oh no, the update failed: ", err.message) }) }) } return { statusCode: 200, body: JSON.stringify({ received: true }), } } catch (err) { console.log(`Stripe webhook failed with ${err}`) return { statusCode: 400, body: `Webhook Error: ${err.message}`, } } } export { handler }
Feb 11, 2022, 4:20 AM
Just a hunch, but you’re probably getting rate limited. The
I think I’d build a transaction with all the patches instead and send it as one request.
Here’s a pattern you can adopt .
lineItems.forEachwill loop super fast and make multiple patch requests to the API and likely hit the limit of 25 requests per second.
I think I’d build a transaction with all the patches instead and send it as one request.
Here’s a pattern you can adopt .
Feb 11, 2022, 4:25 AM
E
Ah okay that makes sense because it is succeding for one item, but failing with multiplen items!
Feb 11, 2022, 4:27 AM
E
Thank you!
Feb 11, 2022, 4:28 AM
E
Yarg! That wasn't it. I followed that code and it is working locally fine - but still getting weird errors in live mode.
Feb 11, 2022, 5:16 AM
E
Also seeing socket hang up errors
Feb 11, 2022, 5:16 AM
E
Ignore the Stripe Webhook signature errors, that is from testing locally and a mismatch in the webhook secret.
Feb 11, 2022, 5:18 AM
E
Any chance it would relate to using the CDN or not?
Feb 11, 2022, 5:22 AM
E
Got it! In my rewrite I missed an await!
Feb 11, 2022, 5:28 AM
E
Thank you
user Y
!Feb 11, 2022, 5:28 AM
Sanity– build remarkable experiences at scale
Sanity is a modern headless CMS that treats content as data to power your digital business. Free to get started, and pay-as-you-go on all plans.