Firebase Cloud function request.body empty or undefined - node.js

const functions = require("firebase-functions");
const Razorpay = require("razorpay");
const cors = require("cors")({origin: true});
exports.razorpayverification = functions.https.onRequest((req, res) => {
try {
const name = JSON.stringify(req.body);
console.log(req.body, name);
res.status(200).json({status: "ok"});
// I need req.body.amount value. req.body is empty. but JSON.stringify(req.body) contains all the data.
} catch (error) {
res.status(400).json({error: "JSON error"});
res.end();
}
});
I am using the above cloud function and receiving payment data from Razorpay webhooks. In my console.log I have called req.body and name (const name = JSON.stringify(req.body);). name returns the data in string format and contains all information like orderid, payment id, amount etc but req.body is empty or undefined. I tried enclosing the above within cors but still the same result. Kindly let me know how to read req.body. I need to read req.body.amount.
Output:
req.body:
{
name: {"entity":"event","account_id":"acc_IvsP3zm1EoP7me","event":"payment.captured","contains":["payment"],"payload":{"payment":{"entity":{"id":"pay_J9fXFPd7T9uWo5","entity":"payment","amount":1000,"currency":"INR","status":"captured","order_id":"order_J9fWySTzmrenBl","invoice_id":null,"international":false,"method":"card","amount_refunded":0,"refund_status":null,"captured":true,"description":"Recharge","card_id":"card_J9fXFSITSAyVdB","card":{"id":"card_J9fXFSITSAyVdB","entity":"card","name":"k","last4":"1111","network":"Visa","type":"debit","issuer":null,"international":false,"emi":false,"sub_type":"consumer","token_iin":null},"bank":null,"wallet":null,"vpa":null,"email":"test#test.com","contact":"+16505551234","notes":{"key1":"otzo","key2":"recharge","address":"Razorpay Corporate Office"},"fee":20,"tax":0,"error_code":null,"error_description":null,"error_source":null,"error_step":null,"error_reason":null,"acquirer_data":{"auth_code":"213319"},"created_at":1647850380}}},"created_at":1647850385}
As shown req.body returns only the open curly braces and nothing after that. name returns all the data.

Not sure this is the correct answer but my code was executing properly and I was receiving the res.body. But issue lies in the console.log which did not display. if it is a multi-level object firebase cloud function is not displaying properly. Below is the Razorpays Payments Sample Payload. res.body displays only open end curly bracket. res.body.payload created log with all the details under the payload section. Again here firebase cloud function displays each and every field as a separate log.
{ "entity": "event", "account_id": "acc_BFQ7uQEaa7j2z7", "event": "payment.authorized", "contains": [ "payment" ], "payload": { "payment": { "entity": { "id": "pay_DESlfW9H8K9uqM", "entity": "payment", "amount": 100, "currency": "INR", "status": "authorized", "order_id": "order_DESlLckIVRkHWj", "invoice_id": null, "international": false, "method": "netbanking", "amount_refunded": 0, "refund_status": null, "captured": false, "description": null, "card_id": null, "bank": "HDFC", "wallet": null, "vpa": null, "email": "gaurav.kumar#example.com", "contact": "+919876543210", "notes": [], "fee": null, "tax": null, "error_code": null, "error_description": null, "error_source": null, "error_step": null, "error_reason": null, "acquirer_data": { "bank_transaction_id": "0125836177" }, "created_at": 1567674599 } } }, "created_at": 1567674606}

Related

Remove object from nested array in MongoDB using NodeJS

I can see that this question should have been answered here, but the code simply doesn't work for me (I have tried multiple, similar variations).
Here is my data:
[{
"_id": {
"$oid": "628cadf43a2fd997be8ce242"
},
"dcm": 2,
"status": true,
"comments": [
{
"id": 289733,
"dcm": 2,
"status": true,
"clock": "158",
"user": "Nathan Field",
"dept": "IT",
"department": [],
"dueback": "",
"comment": "test 1"
},
{
"id": 289733,
"dcm": 2,
"status": true,
"clock": "158",
"user": "Nathan Field",
"dept": "IT",
"department": [],
"dueback": "",
"comment": "test 2"
}
],
"department": [],
"dueback": ""
}]
And here is my code
const deleteResult = await db.collection('status').updateOne(
{ "dcm": comments.dcm },
{ $pull: { "comments": { "id": comments.id } } },
{ upsert: false },
{ multi: true }
);
Absolutely nothing happens...
So the issue ended up being something to do with running multiple update operations within one function. I have a database connection function like this:
const withDB = async (operations, res) => {
try {
const client = await MongoClient.connect('mongodb://localhost:27017', { useNewUrlParser: true });
const db = client.db('collection');
await operations(db);
client.close();
} catch (error) {
res.status(500).json({ message: 'Error connecting to db', error });
}
}
And then I call this by using:
withDB(async (db) => {
await db.collection('status').updateMany(
{ "dcm": comments.dcm },
{ $pull: { "comments": { "id": comments.id } } },
{ multi: true }
);
});
The issue occurred it would seem because I had two of these update operations within one withDB function. I have multiple operations in other instances (update item, then fetch collection), but for some reason this caused an issue.
I created a separate call to the withDB function to perform the '$pull' (delete) request, and then updated the array with the new comments.
To check that there was nothing wrong with my actual query, I used Studio3T's IntelliShell feature. If I'd done that sooner I would saved myself a lot of time!

How to destructure a Stripe object

I have written the following web hook to capture events coming from stripe after a user has amended a subscription:
export const stripeWebhookEvents = functions.https.onRequest( async ( request: any, response: any ) => {
// Retrieve the event by verifying the signature using the raw body and secret.
let event;
const signature = request.headers["stripe-signature"];
try {
event = stripe.webhooks.constructEvent(
request.rawBody,
signature,
STRIPE_WEBHOOK
);
} catch (err) {
console.log("⚠️ Webhook signature verification failed.", err);
return response.sendStatus(400);
}
// Extract the object from the event.
const eventType = event.type;
console.log(`Scripe event type: ${eventType}.`);
// Handle the event
switch (eventType) {
case "customer.subscription.created": {
// Occurs whenever a customer is signed up for a new plan.
console.log("customer.subscription.created");
try {
const session = event.data.object;
console.log("stripe customer: ", session.customer);
const userRecord = await getUserWithStripeCustomerID(session.customer);
console.log("userRecord: ", userRecord);
const data = {
"subscription_status": session.status,
};
await updateUser(userRecord, data);
} catch (error) {
console.log(error);
}
break;
}
case "customer.subscription.updated": {
// Occurs whenever a subscription changes (e.g., switching from one plan to another,
// or changing the status from trial to active).
break;
}
case "customer.subscription.deleted": {
// The subscription has been deleted so update the user document so that it shows
// that the subscription is inactive.
}
case "invoice.payment_failed": {
// The payment failed or the customer does not have a valid payment method.
// The subscription becomes past_due. Notify your customer and send them to the
// customer portal to update their payment information.
// update the user document so that it shows that the subscription is inactive.
break;
}
default: {
// Unexpected event type
console.log(`Unhandled event type ${eventType}.`);
}
}
response.sendStatus(200);
});
The subscription object that is received from stripe is:
{
"id": "sub_JhxDD458se1T1c",
"object": "subscription",
"application_fee_percent": null,
"automatic_tax": {
"enabled": false
},
"billing_cycle_anchor": 1624221102,
"billing_thresholds": null,
"cancel_at": null,
"cancel_at_period_end": false,
"canceled_at": null,
"collection_method": "charge_automatically",
"created": 1624221102,
"current_period_end": 1626813102,
"current_period_start": 1624221102,
"customer": "cus_FJ9HCZk3rCSlxD",
"days_until_due": null,
"default_payment_method": null,
"default_source": null,
"default_tax_rates": [],
"discount": null,
"ended_at": null,
"items": {
"object": "list",
"data": [
{
"id": "si_JhxDPLJExCLmnu",
"object": "subscription_item",
"billing_thresholds": null,
"created": 1624221103,
"metadata": {},
"price": {
"id": "price_1J3q2zHx6drz9dmy2bMdkXOs",
"object": "price",
"active": true,
"billing_scheme": "per_unit",
"created": 1624054821,
"currency": "gbp",
"livemode": false,
"lookup_key": null,
"metadata": {},
"nickname": null,
"product": "prod_JhEWBSIjTi2nlt",
"recurring": {
"aggregate_usage": null,
"interval": "month",
"interval_count": 1,
"usage_type": "licensed"
},
"tax_behavior": "unspecified",
"tiers_mode": null,
"transform_quantity": null,
"type": "recurring",
"unit_amount": 1299,
"unit_amount_decimal": "1299"
},
"quantity": 1,
"subscription": "sub_JhxDD458se1T1c",
"tax_rates": []
}
],
"has_more": false,
"url": "/v1/subscription_items?subscription=sub_JhxDD458se1T1c"
},
"latest_invoice": null,
"livemode": false,
"metadata": {},
"next_pending_invoice_item_invoice": null,
"pause_collection": null,
"pending_invoice_item_interval": null,
"pending_setup_intent": null,
"pending_update": null,
"schedule": null,
"start_date": 1624221102,
"status": "active",
"transfer_data": null,
"trial_end": null,
"trial_start": null
}
When the "customer.subscription.created" I want to update the price id ("price_1J3q2zHx6drz9dmy2bMdkXOs") but I am not sure how to retrieve it. I have tried:
session.items.data[price.id]
but it doesn't work. How do I access the price id?

How to update a value inside mongodb with nodeJS?

I'm trying to update a value inside mogoodb array but is the problem
database?
"TempChannels": [{
"user": "299481590445113345",
"channel": "794869948878159884",
"settings": []
}, {
"user": "583363766750806043",
"channel": "795004103998308352",
"settings": []
}],
The part of the code that should update the user:
Data.TempChannels[0].user = Target.id
Data.save().catch(er => console.log(er))
Also, no error appears when I run the code. and when i console the data it returns a user which has updated but it is not actually saved in mongodb!
code
Data.save().then(() => console.log(Data.TempChannels[0].user))
this is the whole data
{
"_id": {
"$oid": "5ff0cd1ee3d9fd2d40d82d23"
},
"TempChannels": [{
"user": "299481590445113345",
"channel": "795014692522295326",
"settings": []
}, {
"user": "583363766750806043",
"channel": "795015273060892753",
"settings": []
}],
"Guild": "704158258201624657",
"Stats": "true",
"ChannelID": "795014681664290826",
"ParentID": "795014680556994610",
"LogChannelID": "795014683601010709",
"TempControlChannelID": "795014682518749274",
"DefaultSettings": {
"limit": null,
"name": null,
"bitrate": null,
"copyperms": null
},
"__v": 2
}
I'm filtering the data by Guild
if you are using mongoose to connect MongoDB, the
Use markModified("updated field name")
Data.TempChannels[0].user = Target.id
Data.markModified('TempChannels');
Data.save().catch(er => console.log(er))
if you are using mongoose, there is a method that will allow to update the content of existing data
const res = await Person.replaceOne({ _id: 24601 }, { name: 'Jean Valjean' });
res.n; // Number of documents matched
res.nModified; // Number of documents modified
If you are using Mongoose, you can update the record by doing something similar to this:
SchemaName.update({Guild: 'Guild Value Here'}, {$set: { "TempChannels[0].user" : "%newvalue%"}})
.then((data) => {
console.log(data)
})
.catch(err => {
console.log.error(err);
});
You should replace schemaName with the name of your schema if you are using mongoose, and in case you are not using it, I think it would be better for you to start using it.

Getting an empty array on Axios GET request

I'm trying to do a GET request using Axios. The API's response includes several objects that are properly populated. However, one particular object/field is an array that always shows up as an empty array.
Here's the response I get (note the "users" object with the empty array):
{
url: 'https:/<removed>/customers/<removed>/users?number=41442268000',
users: [],
customer: {
url: 'https://<removed>/customers/<removed>',
id: '<removed>',
name: 'CX Customer1'
},
paging: { offset: 0, limit: 2000, count: 0 }
}
The strange thing is that it works perfectly fine when I use Postman to query the exact same resource:
{
"url": "https://<removed>/customers/<removed>/users?number=8013334001",
"users": [
{
"url": "https://<removed>/customers/<removed>/users/<removed>",
"id": "b1703d6a<removed>",
"bwId": "<removed>.webex.com",
"lastName": "One",
"firstName": "Gus1",
"displayName": "Gus1 One",
"type": "USER",
"callerIdLastName": "One",
"callerIdFirstName": "Gus1",
"callerIdNumber": "+1-8013334001",
"numbers": [
{
"external": "+1-8013334001",
"extension": "4001",
"primary": true
}
],
"location": {
"name": "Salt Lake City",
"id": "9a03e3e<removed>",
"url": "https://<removed>/customers/<removed>/locations/<removed>"
}
}
],
"customer": {
"url": "https://<removed>/customers/<removed>",
"id": "4c1ccbe<removed>",
"name": "CX Customer1"
},
"paging": {
"offset": 0,
"limit": 2000,
"count": 1
}
}
As observed in the above Postman response, the "users" array has an object inside of it.
Here's my Node.js code:
function getUsersByTN(customerInfo, userData) {
let rowNumber = 1;
let successCount = 0;
let failureCount = 0;
axios.defaults.headers.common["Authorization"] = `Bearer ${customerInfo.token}`;
console.log('Attempting to find users on Webex Calling using their phone number...');
return new Promise(async (resolve, reject) => {
try {
for (let data of userData) {
rowNumber++;
const phoneNumber = data.TN;
const getUserURL = `https://<removed>/customers/` +
`${customerInfo.customerId}/` +
`users?number=` +
`${phoneNumber}`;
const result = await axios.get(getUserURL);
console.log(result.data);
resolve(result);
}
}
catch (err) {
reject(new Error(err));
}
})
}
I have also tried to replace the async/await format with the more traditional way of using promises, but got the same result:
axios.get(getUserURL)
.then(result => console.log(result.data))
.catch(err => console.log(err));
What am I missing?
Thanks!
-Gus
I found the problem. My input CSV file had users with phone numbers that did not exist. As soon as I updated it with valid/existing phone numbers, it worked as expected.
The thing that threw me off is that the API still replies with a "200 OK" when I provide invalid phone numbers. I was expecting a "404 not found" for invalid numbers, so I didn't even think about checking the numbers.
My first thought was that this was a bug on the API. In other words, I initially thought that the API should reply with a "404 not found". However, as I thought more about this, I realized that a "200 OK" with an empty result is a more appropriate response. That's because a "404" would incorrectly indicate that the API resource could not be found, which would imply that the request was sent to an invalid URL. That's clearly not what we want. Instead, we want to let the application know that it reached a valid API resource, but that no results were found for the provided searched criteria.

How to retrieve metadata from Stripe session object NodeJS

I am trying to pass a UID and purchase ID with Stripe Checkout session object (using metadata). Generating the session ID on my server attaching the metadata works very fine. Stripe also POSTs everything correctly to my webhook server. The problems occurs while retrieving the metadata from the session object POSTed by Stripe.
Here is the error I get
TypeError: Cannot read property 'metadata' of undefined at /app/app.js:35:32
Here is the session obj posted by Stripe-
{
"id": "evt_1GRC7lAfcfWZXl7jQ3VzNo4y",
"object": "event",
"api_version": "2019-10-17",
"created": 1585292221,
"data": {
"object": {
"id": "cs_test_gLsHqtF8XhB3C3DlWKcLtNdTitp0St8ju5qgJgl6tHrMxxWvju9gb9Li",
"object": "checkout.session",
"billing_address_collection": null,
"cancel_url": "https://andropaym.firebaseapp.com/fail.html",
"client_reference_id": null,
"customer": "cus_GzASi1Klpydh8x",
"customer_email": null,
"display_items": [
{
"amount": 37500,
"currency": "inr",
"custom": {
"description": "Carefully modified Linux Distro Bundle for Android.",
"images": null,
"name": "Modded OS Bundle"
},
"quantity": 1,
"type": "custom"
}
],
"livemode": false,
"locale": null,
"metadata": {
"uid": "EB1m6nAOTVNcQhHO2O7COspap8y1",
"payID": "GPA.5620-9852-7063-44324"
},
"mode": "payment",
"payment_intent": "pi_1GRC7EAfcfWZXl7jhixrWHRS",
"payment_method_types": [
"card"
],
"setup_intent": null,
"shipping": null,
"shipping_address_collection": null,
"submit_type": null,
"subscription": null,
"success_url": "https://andropaym.firebaseapp.com/success.html"
}
},
"livemode": false,
"pending_webhooks": 4,
"request": {
"id": null,
"idempotency_key": null
},
"type": "checkout.session.completed"
}
Here is my webhook code -
const express = require('express');
const app = express();
const bodyParser = require('body-parser');
const stripe = require('stripe')('sk_test_xxxx');
const endpointSecret = 'whsec_xxxx';
// set the port of our application
// process.env.PORT lets the port be set by Heroku
var port = process.env.PORT || 8080;
app.post('/', bodyParser.raw({type: 'application/json'}), (request, response) => {
const sig = request.headers['stripe-signature'];
let event;
try {
event = stripe.webhooks.constructEvent(request.body, sig, endpointSecret);
} catch (err) {
return response.status(400).send(`Webhook Error: ${err.message}`);
}
function handleCheckoutSession(uid) {
// Here we are getting the session obj and we can process it to check for the things we need
console.log("UID is " + uid);
}
// Handle the checkout.session.completed event
if (event.type === 'checkout.session.completed') {
const session = event.data.object;
let uid = request.data.metadata.uid;
// Fulfill the purchase...
handleCheckoutSession(uid);
}
// Return a response to acknowledge receipt of the event
response.json({received: true});
});
app.listen(port, function () {
console.log('Our app is running on http://localhost:' + port);
});
module.exports = app;
The code works fine without the metadata being parsed
More code links:
1. Highlighted error webhook code - https://gist.github.com/imprakharshukla/1e2315615983e0e9d492d2288e159832#file-webhook_backend-js-L40
You need to use the object returned by stripe.constructEvent, not the request body.
Change
let uid = request.data.metadata.uid;
to
let uid = session.metadata.uid
and it should work as expected.

Resources