I've been trying to make this function execute every step in the process, but it seems to only be executing everything and returning a 200 response but doesn't run any of the set functions.
This code is triggered by a Stripe Webhook (customer.subscription.created) which calls my endpoint and activates this function.
I can't seem to figure out if the Stripe objects (customer, plan, price, product) are not yet set before my function is called or if my code doesn't work.
const functions = require("firebase-functions");
const axios = require("axios");
class stripeWebhookSubscription {
static createSubscription(database, subscription, res) {
let barcode;
let plan;
database.collection("subscriptions").add({
id: subscription.id,
customer: subscription.customer,
status: subscription.status,
price: {
amount: (subscription.items.data[0].price.unit_amount / 100).toFixed(2),
interval: subscription.items.data[0].price.recurring.interval,
interval_count: subscription.items.data[0].price.recurring.interval_count,
},
product: subscription.items.data[0].price.product,
created: subscription.created,
current_period_start: subscription.current_period_start,
current_period_end: subscription.current_period_end,
cancel_at: subscription.cancel_at,
cancel_at_period_end: subscription.cancel_at_period_end,
payment_gateway: "stripe",
current_usage: 1,
})
.then((doc) => {
barcode = doc.id;
console.log(subscription.items.data[0].price.product);
return database.collection("plans").where("stripe.product", "==", subscription.items.data[0].price.product).limit(1).get()
.then((docs) => {
docs.forEach((doc) => {
console.log(doc.data());
return plan = doc.data();
});
});
})
.then(() => {
database.collection("customers").doc(subscription.customer).set({
merchant: plan.merchant,
subscriptions: [barcode],
}, {merge: true})
.then((doc) => {
return console.log("Customer updated with: merchant (" plan.merchant ") and subscription (" [barcode] ")");
});
})
.then(() => {
database.collection("subscriptions").doc(barcode).set({
merchant: plan.merchant,
}, {merge: true})
.then((doc) => {
return console.log("Subscription updated with merchant: " plan.merchant);
});
})
.then(() => {
// Sends the email confirmation to the customer
database.collection("customers").doc(subscription.customer).get()
.then((doc) => {
const customer = doc.data();
return axios.request({
url: "https://api.sendinblue.com/v3/smtp/email",
method: "post",
headers: {
"api-key": functions.config().sendinblue.key,
"Content-Type": "application/json",
},
data: {
"to": [
{
"email": customer.email,
"name": customer.name,
},
],
"replyTo": {
"email": "[email protected]",
"name": "Scanable",
},
"templateId": 2,
"params": {
"NAME": customer.name,
"SUBSCRIPTION": barcode,
},
},
});
// return console.log("Customer (" customer.email ") email sent!");
});
})
.then(() => {
return res.status(200).send("✅ Subscription " subscription.id " created!");
})
.catch((err) => {
return res.status(400).send("⚠️ Error creating subscription (" subscription.id "): " err);
});
}
}
CodePudding user response:
This is because you are not correctly chaining the different Promises returned by the asynchronous Firestore and Axios asynchronous methods.
The below code adaptations should do the trick.
- In each
then()
block we return the promise returned by the asynchronous method; - We don't create a pyramid of doom of
then()
blocks. If you want to log the fact that an asynchronous method call was successfully executed, do theconsole.log()
in the nextthen()
block. - Finally we don't need to return the call to the Express
send()
method. See the following official video.
let plan;
database.collection("subscriptions").add({
id: subscription.id,
customer: subscription.customer,
status: subscription.status,
price: {
amount: (subscription.items.data[0].price.unit_amount / 100).toFixed(2),
interval: subscription.items.data[0].price.recurring.interval,
interval_count: subscription.items.data[0].price.recurring.interval_count,
},
product: subscription.items.data[0].price.product,
created: subscription.created,
current_period_start: subscription.current_period_start,
current_period_end: subscription.current_period_end,
cancel_at: subscription.cancel_at,
cancel_at_period_end: subscription.cancel_at_period_end,
payment_gateway: "stripe",
current_usage: 1,
})
.then((doc) => {
barcode = doc.id;
console.log(subscription.items.data[0].price.product);
return database.collection("plans").where("stripe.product", "==", subscription.items.data[0].price.product).limit(1).get();
})
.then((docs) => {
// docs is a QuerySnapshot with one element
plan = docs[0].data();
return database.collection("customers").doc(subscription.customer).set({
merchant: plan.merchant,
subscriptions: [barcode],
}, { merge: true })
})
.then(() => {
console.log("Confirmation of previous update")
return database.collection("subscriptions").doc(barcode).set({
merchant: plan.merchant,
}, { merge: true })
})
.then(() => {
// Sends the email confirmation to the customer
return database.collection("customers").doc(subscription.customer).get();
})
.then((doc) => {
const customer = doc.data();
return axios.request({
url: "https://api.sendinblue.com/v3/smtp/email",
method: "post",
headers: {
"api-key": functions.config().sendinblue.key,
"Content-Type": "application/json",
},
data: {
"to": [
{
"email": customer.email,
"name": customer.name,
},
],
"replyTo": {
"email": "[email protected]",
"name": "Scanable",
},
"templateId": 2,
"params": {
"NAME": customer.name,
"SUBSCRIPTION": barcode,
},
},
});
})
.then(() => {
res.status(200).send("✅ Subscription " subscription.id " created!");
})
.catch((err) => {
res.status(400).send("⚠️ Error creating subscription (" subscription.id "): " err);
});