How to handle multiple requests to purchase at same time - node.js

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.

Related

Better approach to store FCM tokens array in a collection/doc and update it on token refresh

I am building an application where people can host events. It allows for users to follow the events and all the event followers can chat in a group. I am storing the FCM token for push notifications in a user collection in Firestore. But when I send the notification my current logic is not quite optimal and it takes several minutes for the notification to send to each user as I am first getting all user's tokens for each user's document and then combing those tokens in a list and send push notification to each token using a loop in my cloud function. I think it takes time for sending the messages and the group chat does not seem to be real-time because of that computation.
What I thought is to store the FCM tokens of each user inside every event he follows. So that when there is a chat message the list of tokens is fetched from the specific event and they are sent a multicast notification. But here again, I am not sure if it is a good approach because I will need to keep track of refreshed tokens and update the tokens in each document where it exists.
Let's say a user has joined 50 events and on app launch, his FCM token got refreshed now I will have to run a cloud function that will see if the token is renewed it should loop through all of those 50 events and update his token.
I want to know what can be the best approach for this use case.
Below is the code that I am using:
exports.sendNotification = functions.firestore
.document("event/{eventid}/{chat}/{chatid}")
.onCreate((snap, context) => {
processData(snap, context);
return null;
});
async function processData(snap, context) {
const doc = snap.data();
const eventID = context.params.eventid;
const senderID = doc.sender;
var senderName = doc.senderName;
await admin
.firestore()
.collection("event")
.doc(eventID)
.get()
.then(value => {
var joinedBy = value.data()["joinedBy"];
joinedBy.forEach(item => {
getTokens(uid, senderID, doc, eventName, senderName);
});
});
}
async function getTokens(uid, senderID, doc, eventName, senderName) {
await admin
.firestore()
.collection("people")
.doc(uid)
.get()
.then(value => {
var token = value.data()["pushToken"];
if (uid != senderID) {
if (token) {
const payload = {
notification: {
title: `${eventName} : ${senderName}`,
body: doc.text,
badge: "1",
sound: "default"
},
};
sendMessage(token, payload);
} else {
console.log("Can not find pushToken target user");
}
} else {
console.log("uid and sender id is same so notification not sent");
}
});
}
One thing to consider in your current approach is whether you really first need to read all tokens for the message and only then starting calling FCM. Might things get faster if you call FCM right away on each set of tokens you read, and thus perform some of the reads and FCM calls in parallel?
Another consideration is that you say that FCM's built-in topics work really fast for you. Internally FCM's topic subsystem associates a bulk of device IDs with the topic, and then reads the tokens for the topic as needed.
Is this something you can mimic for your app? For example, can you store the tokens in larger groups inside fewer documents, thus reducing the number of read operations you need to do.

Capture PayPal Order

When integrating with PayPal's V2 Orders API using the NodeJS SDK, when is the correct time to fulfil the order (e.g. ship a product to the customer).
async function captureOrder(orderId) {
try {
const request = new checkoutNodeJssdk.orders.OrdersCaptureRequest(orderId);
request.requestBody({});
const response = await payPalClient.client().execute(request);
// Is it safe to assume, if the above line didn't reject, the order has been successfully captured?...or do we need to verify the status of the order / capture etc.
return response;
}
catch (e) {
console.log(e)
}
}
If you want to do a positive check before shipment, you could check for a status of COMPLETED, which is the value documented in the API reference for success https://developer.paypal.com/docs/api/orders/v2/#orders_capture
A recoverable funding failure issue of INSTRUMENT_DECLINED, and any non-recoverable situations, should be dealt with according to the logic in https://developer.paypal.com/demo/checkout/#/pattern/server

Inconsistent - "The project id used to call the Google Play Developer API has not been linked in the Google Play Developer Console."

So here's the thing - I have a node.js backend server for my Android App. I am using the Google Play billing library, and using the backend to verify the purchase as google Docs recommend.
Now, all the other answers out there regarding this error seem to refer to a consistent problem.
My backend SOMETIMES verifies, and SOMETIMES comes back with this as an error, indicating that in fact, my service account IS linked (as shows up in my consoles).
I tried two different 3rd party libraries, and I have the same issue. Sometimes one will respond with verification success, while the other will say my account is not linked. Sometimes they are both negative, sometimes both positive.
It seems inconsistent.
var platform = 'google';
var payment = {
receipt: purchaseToken, // always required ... this is google play purchaseToken
productId: subID, // my subscription sku id
packageName: 'com.xxxxxx', // my package name
keyObject: key, // my JSON file
subscription: true, // optional, if google play subscription
};
var promise2 = iap.verifyPayment(platform, payment, function (error, response) {
/* your code */
if (error) {
console.log('error with iap, ' , error);
return true;
} else {
console.log('success with iap, response is: ', response);
return true;
}
});
I also tried with a different library, got same results:
var receipt = {
packageName: "com.xxxx",
productId: subID, // sku subscription id
purchaseToken: purchaseToken // my purchase token
};
var promise = verifier.verifySub(receipt, function cb(err, response) {
if (err) {
console.log('within err, was there a response? : ', response);
console.log('there was an error validating the subscription: ', err);
//console.log(err);
return true;
} else {
console.log('sucessfully validated the subscription');
// More Subscription info available in “response”
console.log('response is: ', response );
return true;
}
});
// return promises later.
Any else experience this issue?
TLDR; Create a new product ID.
I eventually found the answer. The problem was not with my code, or with permissions in the Google Developer Console OR the Google Play Console. Everything was set up correctly except for one thing.
Previously, before setting up Test License Accounts in Google Play Console, I had made an actual Subscription purchase with real money on my productID "X".
Then, after adding the same google account that bought the subscription as a test user, I continued to test results on the same subscription, productID "X".
Even though I had cancelled the REAL purchase, the actual expiration date was not for another month.
Therefore, I believe sometimes Google was getting confused when I would buy/cancel the purchase - confusing the test subscription with the real subscription.
Creating a new Product ID, and only using that, solved my problem, and purchases are verified consistently.

Live Notification - DB Polling - Best Practice

I'm providing my users with live notifications.
I'm debating two options and can't decide which is the best way to go when polling the DB.
(The notifications are transmitted using WebSockets.)
Option 1 (current):
I hold a list of all the logged in users.
Every 1000 ms, I check the db for new notifications and if there is any, I send a message via WS to the appropriate user.
Pros:
This task is rather not expensive on resources
Cons:
In off-times, where's there's only a new notification every 1 minute, I poll the DB 60 times for no reason.
In a sense, it's not real-time because it takes 1 full second for new notifications to update. Had it been a chat service, 1 second is a very long time to wait.
Option 2:
Create a hook that whenever a new notification is saved (or deleted), the db get polled.
Pros:
The db does not get polled when there are no new notifications
Actual real-time response.
Cons:
In rush-hour, when there might be as many as 10 new notifications generated every second, the db will be polled very often, potentially blocking its response time for other elements of the site.
In case a user was not logged in when their notification was generated, the notification update will be lost (since I only poll the db for logged in users), unless I also perform a count whenever a user logs in to retrieve their notifications for when they were offline. So now, not only do I poll the DB when ever my notification hook is triggered, but also I poll the db every time a user logs-in. If I have notifications generated every second, and 10 log-ins every second, I will end up polling my DB 20 times a second, which is very expensive for this task.
Which option would you choose? 1, 2? or neither? Why?
Here is the code I am currently using (option 1):
var activeSockets = [] //whenever a user logs in or out, the array gets updated to only contain the logged-in users in any given moment
var count = function () {
process.nextTick(function () {
var ids = Object.keys(activeSockets) //the ids of all the logged in users
//every user document has a field called newNotification that updates whenever a new notification is available. 0=no new notifications, >0=there are new notifications
User.find({_id:{$in:ids}}).select({newNotifications:1}).lean().exec(function (err,users) {
for(var i=0;i<users.length;i++) {
var ws = activeSockets[String(users[i]._id)]
if(ws!=undefined) {
if (ws.readyState === ws.OPEN) {
//send the ws message only if it wasn't sent before.
if(ws.notifCount!=users[i].newNotifications) {
ws.send(JSON.stringify({notifications:users[i].newNotifications}));
activeSockets[String(users[i]._id)].notifCount = users[i].newNotifications
}
}
else {
//if the user logged out while I was polling, remove them from the active users array
delete activeSockets[String(users[i]._id)]
}
}
}
setTimeout(function () {
count()
},1000)
})
})
}
The implementation of Option 2 would be just as simple. Instead of calling
count()
using
setTimeout()
I only call it in my "new notification", "delete notification", and "log-in" hooks.
Code:
var activeSockets = [] //whenever a user logs in or out, the array gets updated to only contain the logged-in users in any given moment
var count = function () {
process.nextTick(function () {
var ids = Object.keys(activeSockets) //the ids of all the logged in users
//every user document has a field called newNotification that updates whenever a new notification is available. 0=no new notifications, >0=there are new notifications
User.find({_id:{$in:ids}}).select({newNotifications:1}).lean().exec(function (err,users) {
for(var i=0;i<users.length;i++) {
var ws = activeSockets[String(users[i]._id)]
if(ws!=undefined) {
if (ws.readyState === ws.OPEN) {
//send the ws message only if it wasn't sent before.
if(ws.notifCount!=users[i].newNotifications) {
ws.send(JSON.stringify({notifications:users[i].newNotifications}));
activeSockets[String(users[i]._id)].notifCount = users[i].newNotifications
}
}
else {
//if the user logged out while I was polling, remove them from the active users array
delete activeSockets[String(users[i]._id)]
}
}
}
//setTimeout was removed
})
})
}
Hooks:
hooks = {
notifications : {
add: function () {
count()
//and other actions
},
remove: function () {
count()
//and other actions
}
},
users: {
logIn: function () {
count()
//and other actions
}
}
}
So, Which option would you choose? 1, 2? or neither? Why?

How do i update a user's subscription date using stripe webhooks?

I'm building a subscription plan in node.js, I read the documentation about how to subscribe a user to a plan, and it was successful.
Stripe's doc states that I have to store an active_until field in the database. It says when something changes use webhook, i know that webhook is like an event.
The real questions are
1) how do I do a repeat the bill every month using active_until?
2) How do I use webhook, I really don't understand.
Here's the code so far.
var User = new mongoose.Schema({
email: String,
stripe: {
customerId: String,
plan: String
}
});
//payment route
router.post('/billing/:plan_name', function(req, res, next) {
var plan = req.params.plan_name;
var stripeToken = req.body.stripeToken;
console.log(stripeToken);
if (!stripeToken) {
req.flash('errors', { msg: 'Please provide a valid card.' });
return res.redirect('/awesome');
}
User.findById({ _id: req.user._id}, function(err, user) {
if (err) return next(err);
stripe.customers.create({
source: stripeToken, // obtained with Stripe.js
plan: plan,
email: user.email
}).then(function(customer) {
user.stripe.plan = customer.plan;
user.stripe.customerId = customer.id;
console.log(customer);
user.save(function(err) {
console.log("Success");
if (err) return next(err);
return next(null);
});
}).catch(function(err) {
// Deal with an error
});
return res.redirect('/');
});
});
How do i Implement the active_until timestamps and the webhook event?
active_until is just the name of a database column that you can create on your users table to store the timestamp representing when the user's account expires. The name of the column doesn't matter. You can use any name you want.
In order to verify if a user's subscription is current, Stripe is suggesting that you use logic like this:
If today's date <= user.active_until
allow them access
Else
show them an account expired message
The webhook is a request that Stripe's servers make to your server to tell you that something has happened. In this case, the event you are most interested in is invoice.payment_succeeded.
Your webhook will include logic like this:
if event type is "invoice.payment_succeeded"
then update user.active_until to be equal to today's date + 1 month
You will also want to respond to other events in case the payment fails, etc.
You don't need to repeat the bill every month. Stripe will do it for you.
Once you subscribed your user to a plan, stripe will charge him till the paid period is over.
Every time stripe charges a customer it will generate a webhook, that is a request on your server to some specified URL. Stripe can generate different webhooks for different reasons.
For example when customer gets charged by subscription, Stripe will send you info about payment.
router.post('/billing/catch_paid_invoice', function(req, res) {
// Here you parse JSON data from Stripe
}):
I don't have access to Stripe settings right now, but a remember setting urls for webhooks manually.
Select your account name > Account Settings > Webhooks
active_until is just a reminder that customer is still active and have paid services in your system. It needs to be updated when getting webhooks.
Stripe docs are really good, so go through them one more time.
https://stripe.com/docs/guides/subscriptions

Resources