NodeJS/Firebase Cloud Functions - Generate Percentage Fee - node.js

I'm implementing Stripe into my app, however, I want to generate the fee amount via server side as this can allow me to update as and when. I've tried to have a go, but to no luck.
exports.calculateFees = functions.https.onRequest((req, res) => {
var total = req.body
var percentageFeeCharge = 10
var feeAmount = 0
feeAmount = ((percentageFeeCharge/ 100) * total))
return res.send(feeAmount)
});
I can pass in the total from my app to the function which is cool, however, I need the amount to be generate with no decimals or symbols as Stripe deals with pennies i.e. £1.00 would equate to 100 etc.
I believe the res() is the response which is what I need to get the amount back, I believe so? I'm new to NodeJS so this may be simple, but I am struggling.

Related

Are "get" functions in Google cloud functions for firebase reading each document everytime?

I observed a huge amount of read on my firebase console and I was wondering if this might come from my "referral function".
This function works perfectly fine but I was wondering whether or not this function could end up with a crazy load of read in case of app scaling.
My question: does this function imply that every time a user comes in, it will account for a number of read equivalent to the number of users in my collection ?
Thus, as this function is an onUpdate, will it redo the job every time a document is updated ?
I would not mind some resources on the topic because I found it unclear on Firebase's website.
I hope my questions are clear!
Thank you very much!
export const onReferralInfoUpdate = functions.
firestore.document('users/{userUid}')
.onUpdate(async (change, context) => {
const before = change.before.data();
const after = change.after.data();
const currentUserUid = after["uid"];
if (before.godfather_code == after.godfather_code){
console.log('Text did not change')
return null
}
const godfatherUserSnapshot = await db.collection('users').where("referral_code", "==", after.godfather_code).get();
const godfather = godfatherUserSnapshot.docs[0].data();
const godfatherUid = godfather["uid"];
const userRef = db.collection('users').doc(after.uid);
const godfather_code = after.godfather_code
await userRef.update({godfather_code})
console.log(`the text before was >> ${before.godfather_code} << and after is ${after.godfather_code}` )
let batch = db.batch();
const updateGodfather = db.collection('users').doc(godfatherUid);
batch.update(updateGodfather, {
reward: admin.firestore.FieldValue.increment(100),
godChildUid: admin.firestore.FieldValue.arrayUnion(currentUserUid),
});
return batch.commit();
});
Yes, the where("referral_code", "==", after.godfather_code).get() will fetch all the documents matching the query every time onUpdate() function triggers and you'll be charged N reads (N = number of matched documents). The Admin SDK doesn't have any caching like Client SDKs.
Does this function imply that every time a user comes in, it will account for a number of read equivalent to the number of users in my collection ?
Not numbers of documents in the users collection, only the documents matching your query as mentioned.

Get and increment in Firebase Realtime

I'm making a Firebase function, that is supposed to get the value of a field in the Realtime Database, write the value in a Firestore Document and increment the original field. The problem is when the function gets called very frequently e.g. 500 times a second, it gets and writes the same value in a lot of documents, because many executions will get the same value before it gets incremented. Is there any way to get the value of a Realtime DB field and increment it at the same time or somehow prevent this issue?
Thank you in advance.
My code:
const { getFirestore } = require('firebase-admin/firestore');
const { getDatabase, ServerValue } = require('firebase-admin/database');
const rb = getDatabase();
const db = getFirestore();
exports.increment = functions.https.onCall(async (data, context) => {
rb.ref('count').get().then((snapshot)=>{
let value = snapshot.val();
db.collection("documents").doc(value.toString()).set({count:value});
rb.ref("count").set(ServerValue.increment(1))
})
});
Since you're using an auto-scaling infrastructure with Cloud Functions, it will spin up new instances if there are a lot of requests coming in. If you don't want to do that, it might be worth setting a maximum number of instances on your Cloud Function.

how to delete anonymous users that last signed in more than certain time from Firebase Authentication in NodeJS Admin SDK?

from Firebase Authentication, we have table like this, the providers can be email, google, facebook or anonymous
I need to delete all anonymous accounts that last signed in was more than six months ago. I need to query those anonymous accounts and then delete them all.
but I really have no idea how to query all anonymous account that last signed in was more than six months ago using Node JS admin SDK. is it possible? how to do that?
because there is a limit from firebase (100 million anonymous account) from the documentation in here. I may not hit that limit, but I think it is better If I can clean unused anonymous accounts by creating a cron job using cloud scheduler in cloud function
I think I find the solution, I suggest you to read this Firebase official documentation first, to know how to get all users from authentication, there is an explanation there that you need to read. there is no something like query to get data we need, at least right now
I will use Typescript and async/await instead of then/catch and Javascript. but if you use javascript, then you just need to modify the function parameter a little bit
import * as moment from "moment";
const auth = admin.auth();
export const deleteUnusedAnonymousUsers = async function(nextPageToken?: string) {
// this is a recursive function
try {
// get accounts in batches, because the maximum number of users allowed to be listed at a time is 1000
const listUsersResult = await auth.listUsers(1000, nextPageToken);
const anonymousUsers = listUsersResult.users.filter((userRecord) => {
return userRecord.providerData.length == 0;
});
const sixMonthAgo = moment().subtract(6, "months").toDate();
const anonymousThatSignedInMoreThanSixMonthAgo = anonymousUsers.filter((userRecord) => {
const lastSignInDate = new Date(userRecord.metadata.lastSignInTime);
return moment(lastSignInDate).isBefore(sixMonthAgo);
});
const userUIDs = anonymousThatSignedInMoreThanSixMonthAgo.map((userRecord) => userRecord.uid);
await auth.deleteUsers(userUIDs);
if (listUsersResult.pageToken) {
// List next batch of users.
deleteUnusedAnonymousUsers(listUsersResult.pageToken);
}
} catch (error) {
console.log(error);
}
};
usage
deleteUnusedAnonymousUsers();

How to handle multiple requests to purchase at same time

Let's imagine we have a shopping website which the users are able to purchase items with their account-balance.
user A requests to purchase item B.
here are the steps:
Database gets user's balance.
Checks if balance is more than the cost.
Updates user's balance ( balance - cost = newBalance )
now this is where my question begins:
since javascript is single-threaded and we are requesting to db in an asynchronous way what happens if:
user A sends multiple requests to purchase the item
another user tries to purchase the item ( item should be out of stock after 1 purchase )
I've done some testing and I want to know what is the best way to prevent userA to purchase multiple items when in reality he should be out of balance after second purchase?
my test:
const fs = require('fs');
const cost = 500;
// .data.txt has `1000` as its content
function getMoneyFromDatabase() {
return new Promise((resolve, reject) => {
fs.readFile('./data.txt', 'utf-8', (err, data) => {
if (err) reject(err);
else resolve(Number(data));
});
});
}
function setMoneyToDatabase(newMoney) {
return new Promise((resolve, reject) => {
fs.writeFile('./data.txt', newMoney, (err) => {
if (err) reject(err);
else resolve();
});
});
}
async function getMoney() {
const money = await getMoneyFromDatabase();
if (money >= cost) {
// Able to purchase
console.log('able to purchase');
const newMoney = money - cost;
await setMoneyToDatabase(newMoney);
console.log('purchased');
}
}
getMoney();
getMoney();
getMoney();
getMoney();
this logs 4 purchased while in reality it should only be able to purchase two times
and the data saved inside data.txt is 500 while it should be -1000 ( 500, 0, -500, -1000 )
One thing you can do is make sure your API requests related to charging are idempotent, meaning they can be sent multiple times but will result in the same outcome as long as the request is the same.
You can implement idempotent requests by sending a unique key along with the request to make sure that only the first time it will be accepted as a new request otherwise it will be handled as a duplicate request. This is also useful when you want to safely retry a requests without accidentally charging the user multiple times.
Many payment processors have built-in support for this such as Stripe: https://stripe.com/docs/api/idempotent_requests
As for stock, you should only decrease it when a payment is verified or if it's not automatic the first user with a pending invoice for it. Otherwise the stock will go down even if the user for example has the balance but payment could not be processed.
Implementing payments is hard and can only be learned to be implemented in a decent way after trial and errors, I'd recommend you try to do a minimal app and handle payments with a payment gateway such as Stripe and advance by handling edge cases.

How to query firebase realtime database in cloud code

I am using Firebase cloud code and firebase realtime database.
My database structure is:
-users
-userid32
-userid4734
-flag=true
-userid722
-flag=false
-userid324
I want to query only the users who's field 'flag' is 'true' .
What I am doing currently is going over all the users and checking one by one. But this is not efficient, because we have a lot of users in the database and it takes more than 10 seconds for the function to run:
const functions = require('firebase-functions');
const admin = require("firebase-admin");
admin.initializeApp(functions.config().firebase);
exports.test1 = functions.https.onRequest((request, response) => {
// Read Users from database
//
admin.database().ref('/users').once('value').then((snapshot) => {
var values = snapshot.val(),
current,
numOfRelevantUsers,
res = {}; // Result string
numOfRelevantUsers = 0;
// Traverse through all users to check whether the user is eligible to get discount.
for (val in values)
{
current = values[val]; // Assign current user to avoid values[val] calls.
// Do something with the user
}
...
});
Is there a more efficient way to make this query and get only the relevant records? (and not getting all of them and checking one by one?)
You'd use a Firebase Database query for that:
admin.database().ref('/users')
.orderByChild('flag').equalTo(true)
.once('value').then((snapshot) => {
const numOfRelevantUsers = snapshot.numChildren();
When you need to loop over child nodes, don't treat the resulting snapshot as an ordinary JSON object please. While that may work here, it will give unexpected results when you order on a value with an actual range. Instead use the built-in Snapshot.forEach() method:
snapshot.forEach(function(userSnapshot) {
console.log(userSnapshot.key, userSnapshot.val());
}
Note that all of this is fairly standard Firebase Database usage, so I recommend spending some extra time in the documentation for both the Web SDK and the Admin SDK for that.

Resources