Im trying to pass metadata (userUid) to the Webhook so it saves it correctly in my Firebase.
the Checkout Session receives the userUid correctl (seen in the console.log) but somehow the metadata is undefined in the Webhook.
Here is the cretae-Checkout-Session code:
app.post('/create-checkout-session', async (req, res) => {
const prices = await stripe.prices.list({
lookup_keys: [req.body.lookup_key],
expand: ['data.product'],
});
const userUid = req.body.user_uid;
console.log("Received form data:", req.body);
console.log("Received user_uid:", req.body.user_uid); //received Correctly
console.log("Useruid :", userUid); //Logged correctly
const session = await stripe.checkout.sessions.create({
billing_address_collection: 'auto',
line_items: [
{
price: 'price_1Oi4pfKIP1dcMwGFpd8tLgP2',
// For metered billing, do not pass quantity
quantity: 1,
},
],
mode: 'subscription',
success_url: `${YOUR_DOMAIN}/admin/create`,
cancel_url: `${YOUR_DOMAIN}?canceled=true`,
metadata: {
userUid: userUid, // Passing userUid as metadata
},
});
res.redirect(303, session.url);
});
And here is my webhook:
app.post(
'/webhook',
express.raw({ type: 'application/json' }), // Ensure the body is received as a raw Buffer
async (request, response) => {
const signature = request.headers['stripe-signature'];
let event;
let subscription;
console.log(typeof request.body); // This should log 'object', indicating it's a Buffer
if (endpointSecret) {
try {
// Construct the event using the raw body and signature
event = stripe.webhooks.constructEvent(
request.body,
signature,
endpointSecret
);
} catch (err) {
console.log(`⚠️ Webhook signature verification failed.`, err.message);
return response.status(400).send(`Webhook Error: ${err.message}`);
}
} else {
// If there's no endpoint secret, you could parse the body to work with it as a JSON object
// Note: This is not recommended for production as it bypasses signature verification
event = JSON.parse(request.body.toString());
}
response.json({received: true});
switch (event.type) {
case 'customer.subscription.trial_will_end':
subscription = event.data.object;
status = subscription.status;
console.log(`Subscription status is ${status}.`);
// Then define and call a method to handle the subscription trial ending.
// handleSubscriptionTrialEnding(subscription);
break;
case 'customer.subscription.deleted':
subscription = event.data.object;
status = subscription.status;
console.log(`Subscription status is ${status}.`);
// Then define and call a method to handle the subscription deleted.
// handleSubscriptionDeleted(subscriptionDeleted);
break;
case 'customer.subscription.created':
subscription = event.data.object; // Define subscription here
userUid = subscription.metadata.userUid;
try {
await db.doc(`restaurant/${userUid}/branch/payment`).set({
model: "pioneer",
paid: true,
}, { merge: true });
console.log(`Firestore updated for ${userUid} with subscription `);
} catch (error) {
console.error("Error updating Firestore:", error);
response.status(500).send(`Error updating Firestore: ${error.message}`);
return;
}
console.log(`Handled customer.subscription.created for subscription ${subscription.id}`);
break;
in the log it says : Firestore updated for undefined with subscription
>Solution :
Currently you are saving metadata on the Checkout Session object not on the Subscription. Since the webhook event you are listening to is customer.subscription.created, you’ll need to set metadata here instead to have it set on the Subscription object and receive it in the customer.subscription.created event.