PLEASE help us help you by writing a good post!
- we need to know your netlify site name. Example:
kaselartnextjsdev.app.netlify.com
I am building a website for a friend to sell his artwork. I have been trying to get over this problem for a couple weeks now and it is time to task for help!
Context: When a purchase is completed through Stripe Checkout, I have a Stripe Webhook set up to make a call to a Netlify serverless function which in turn calls the Airtable API to decrement the inventory of the product purchased by 1.
The webhook sends an event with the id associated with the product in Airtable. The serverless function then finds the airtable record with that id and then updates the record, decrementing it by 1.
At first, this functionality seemed to work in production, but not 100% of the time. I then tested in development with the Stripe CLI and Netlify CLI. Triggering the webhook there works and I always see the inventory decrease immediately in Airtable.
Problem: In production, I can see that the flow is working as expected until the point when the update request is sent to Airtable. It seems at this point the code stops executing. Again, this does not happen in development in the CLI tools.
Code: I will add the serverless function responsible for receiving the webhook information as well as the development logs (CLI) and production logs (Netlify App) to show where the code stops running.
NETLIFY SERVERLESS FUNCTION
const stripe = require("stripe")(process.env.STRIPE_SECRET_KEY);
const Airtable = require("airtable");
Airtable.configure({
endpointUrl: "https://api.airtable.com",
apiKey: process.env.AIRTABLE_API_KEY,
});
const base = Airtable.base(process.env.AIRTABLE_BASE_ID);
const table = base(process.env.AIRTABLE_TABLE_NAME);
const decrementInventory = (record, newInventory) => {
console.log("IN DECREMENT FUNCTION");
console.log(record, newInventory);
table.update(
[
{
id: record,
fields: {
inventory: newInventory,
},
},
],
function (err, records) {
if (err) {
console.log("ERROR");
console.error(err);
}
records.forEach(function (record) {
console.log("END INVENTORY:");
console.log(record.get("inventory"));
});
}
);
};
exports.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") {
console.log("CHECKOUT SESSION COMPLETED");
const eventObject = stripeEvent.data.object;
const id = eventObject.metadata.airtableId;
console.log(id);
// const testId = "recBaKuw8TY27ndSj";
const record = await table.find(id);
const prevInventory = record.fields.inventory;
console.log(prevInventory);
if (prevInventory > 0) {
console.log("inventory greater than 0");
decrementInventory(record.fields.idCalculation, prevInventory - 1);
}
}
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}`,
};
}
};
CLI LOGS (successful)
Request from ::1: POST /.netlify/functions/decrement-inventory
Response with status 200 in 4 ms.
Request from ::1: POST /.netlify/functions/decrement-inventory
Response with status 200 in 4 ms.
Request from ::1: POST /.netlify/functions/decrement-inventory
Response with status 200 in 4 ms.
Request from ::1: POST /.netlify/functions/decrement-inventory
Response with status 200 in 5 ms.
Request from ::1: POST /.netlify/functions/decrement-inventory
CHECKOUT SESSION COMPLETED
undefined
5
inventory greater than 0
IN DECREMENT FUNCTION
recBaKuw8TY27ndSj 4
Response with status 200 in 505 ms.
END INVENTORY:
4
PRODUCTION LOGS (unsuccessful)
Feb 27, 05:56:38 PM: d9ebc1bf INFO CHECKOUT SESSION COMPLETED
Feb 27, 05:56:38 PM: d9ebc1bf INFO recBaKuw8TY27ndSj
Feb 27, 05:56:38 PM: d9ebc1bf INFO 4
Feb 27, 05:56:38 PM: d9ebc1bf INFO inventory greater than 0
Feb 27, 05:56:38 PM: d9ebc1bf INFO IN DECREMENT FUNCTION
Feb 27, 05:56:38 PM: d9ebc1bf INFO recBaKuw8TY27ndSj 3
Feb 27, 05:56:38 PM: d9ebc1bf Duration: 219.65 ms Memory Usage: 71 MB Init Duration: 328.72 ms
Disclaimer: I’m still pretty new to http requests and serverless functions so it’s causing me a lot of headache. I thought perhaps the code executes differently in production and is executing after the table.update before it has time to finish, but I’m really not sure as I imagine that would happen in the development environment as well…
Thanks for reading! If you can help or point me in the right direction I would appreciate it.