Stripe API call is producing multiple subscriptions when using Node.js - javascript

I am implementing the stripe api in my node application. The problem i am having is that if i click the submit button very fast multiple times, the api is called multiple times and the stripe customer suddenly has multiple subscriptions and charges.
The scenario is when a user has a stripe customer account without a subscription since they have previously unsubscribed. Now they would like to resubscribe with a new plan since the old one is deleted.
The logic of my code is as follows:
1. Submit form
2. I retrieve the user from my mongo db
3. I retrieve the customer from stripe using a stored customer id (api call)
4. I create a customer subscription (api call)
5. I update the stripe customer object with their credit card (api call)
6. Respond back to user
All of the above is done using async waterfall, each subsequent asynchronous call is in a separate function
What I want:
Between steps 3 and 4 i want to retrieve the customer and check if he or she is already subscribed and prevent 4 and 5 from occurring if the user already has an active subscription.
The Issue:
I am testing the scenario where the user clicks the submit button multiple times and ends up being charged for several subscriptions. I believe that it has to do with how node sends several requests at once due to its asynchronous nature and me wanting to check make an api call to check if something is set takes too long and node doesn't wait when it sends all of the requests.
How do i go about solving this.
Note: Of course i have the front end handling this to prevent user from submitting the form multiple times but this is not ideal and don't want this to be my only line of defense.
Thanks

use a powerful rate limiter library
https://github.com/jhurliman/node-rate-limiter
var RateLimiter = require('limiter').RateLimiter;
// Allow 1 requests per minute
var limiterPayments = new RateLimiter(1, 'minute');
exports.payWithStripe = function(req,res){
limiterPayments.removeTokens(1,function(err,remainingRequests){
if (err){
return res.sendStatus(429);
} else {
// continue with stripe payment
}
}
This will throttle per session (per user browser)

One way is to keep a cache (in Redis or similar) of recent transactions, indexed by a hash derived from key transaction values (e.g. customer id, date, time, amount.)
Before submitting the subscription request to stripe, compare the current transaction to the cache. If you get a hit, you can either show an error, automatically ignore the transaction, or let the user choose whether to proceed.

Disable the button in your frontend using javascript as soon as it's clicked the first time.

Stripe has built in functionality to help you prevent this. https://stripe.com/docs/api/idempotent_requests
You need to generate a unique string and send it along with the stripe request.
Putting it in the HTML in a hidden form, a la your CORS token, then reading it out and putting it in the Stripe call will prevent double click / retries.

Related

How to prevent shopping cart alterations in another tab when paymentintent is already created

Has anyone figured out a solution to this? I seem to have gotten to the same conclusion with no solution.
If I were to go the my app's checkout page, the payintent is created in the backend (explained the process below). So no after the payIntent is created, if i open a new tab and go the menu and add a new menu item, firestore will show the new (correct) total, but since the payment intent is created stripe charges the old (wrong) total.
What I am doing is
Every time the page loads, I send a GET request to my backend which verifies the identity of the user (using firestore/firebase).
Checks if there is a payment intent (payement intents are stored in firestore corresponding to the user)
A. if payintent does not exist under user create one
B. if payintent does exist retrieve payintent from stripe and check if it has .status===succeeded. IF it has succeeded create a new one and if it has not succeeded update the old one. The amount for all payIntents is calculated using total in firestore
(and ofc if the users cart is empty a payintent is not created)
Send back to the frontend the payInent.clienSecret and cart items to populate page
From the front end using stripe elements and confirmPayment confirm the payment
(using ngrok the page loads in about 800-1200ms so not too bad i think)
Possible solutions are using webhooks, when payintent is processing check and update the pricing but that seems like duct taped solution (if it were to even work). OR using webhooks when payment has succeeded update the payment, again seems like a duct tape solution (if it were to even work).
EDIT: possible solution 3 cofirmPayment in the backend but according to documentation that takes away 3ds authentication which is the reason I am doing confirmPayment in the front end anyways
SOLUTION: The missing piece is that you need to update the Payment Intent's amount when something is added to their cart. Once you add that I think that will solve your issue.
So a function to create payment intent and update payment intent (when there is already a payment intent created) on add to cart. And then a final update paymentIntent on the checkout page whenever they delete an item or if they edit the item
Thank you Justin Michael
I'm not sure I completely understand your question. If you confirm a Payment Intent client-side using its client secret the Payment Intent will attempt to charge whatever the current amount set on it is. Stripe will never use a previous or "old" amount.
As far as a solution, I recommend you retrieve the Payment Intent client-side using Stripe.js when your customer clicks on your "pay" button and see if the currently-set amount on the Payment Intent matches what you're currently displaying to them. If it doesn't match abort the payment process, update your state client-side based on the latest version of the Payment Intent you just retrieved, prompt the customer to confirm the new amount, and ask them to click on "pay" again.

j3k0 cordova plugin purchase android transaction receipt is not updated

I am trying to set up j3k0 Plugin Purchase for subscriptions on Android and somewhat have it working.
I register my products and then call store.refresh()
On my products page I call store.get('my_id') and retrieve the products which I display
On purchase I call store.order('my_id') which does initiate the billing popup. I confirm and the first time everything appears fine
I have store.when('my_id').approved(function(data) { my code in here }); which does get called and worked the first time. Now every time I try to test it I get the same data.transaction back so I keep referencing the original receipt data which is cancelled when I try to validate on my server.
store.when('my_id').approved(function(data) {
var receiptIsAlwaysTheSame = data.transaction.receipt;
//I send the receipt to my server here to validate
});
I do get valid test emails from the store every time I subscribe or cancel so not quite sure why the transaction seems to be cached. I did see this older issue with iOS on the GitHub site which just recently went on the backlog and not sure if it is similar or what: Receipt Validation
UPDATE
So after 3 days I made another purchase and the method gave me back a new valid receipt with new transaction data. My first thought was the grace period on my product, but that is set to 0 days anyway. So still not sure why I have to wait 3 days before making another purchase. If anybody knows how to force this to refresh before then that would be great.
I'm trying to implement my own server for validating non-renewing subscriptions, in order that these can be restored on new app install etc. I'd like to be able to do it without collecting a unique ID from the user e.g. email address. Is that possible?

Stripe webhooks events order

I'm currently building a node application that's using Stripe for payments. I've got webhooks setup and working as I want to create subscribers in my application but require the response from the stripe webhooks to store the data received for different events in different collections in my mongo db.
The issue I'm having is that the order of events sent by Stripe is not always in the same order and in order to create relationships between documents/collections I require that the event handlers are triggered in the following order:
customer.created
customer.card.created (relates to customer)
invoice.created (relates to customer)
As it stands the event handler for 2 can be executed before 1 and 3 before 2 etc.
What would be the best way to ensure my handlers are executed in the correct order every time? I'm thinking promises of some-sort. If this is the case, what's a good promise module for node?
You can reject hook if an event is missing, stripe will try it later but if you want it to happen quick, you can make a small loop waiting for max 10 seconds and who checks every second if missing event is arrived).
I have same problem when updating a billing plan, the event invoice.updated arrives before invoice.created ...
Why don't you just use the id of the stripe object as the foreign key on your document? Don't store the actual json object, just the stripe id. When you need to query for the documents, build your result set by using the stripe id as the primary key.

Nodejs multiple request in a row

I'm developing nodejs application with users registration using sails.
I have an /signup method, which is looking for existing user, if user not found, it will create new user in db.
As you know in sails model (waterline) available few methods : beforeCreate, afterCreate. In beforeCreate I need also register user in a few other services(it takes +-2 seconds), after that I can set received data to user object.
The problem is if user clicks few times on "register" button (or just send the same request 2+ times in a row), it will create 2 objects in db ( or another services ). I have a similar with some other methods like this.
I was trying to set unique field in user model. Also maybe is good idea to implement police, witch can check user + reqest url and put this data into redis. If the second request arrives , check if it exists in Redis. Any other ideas?

Handling successful payment processing but database update failure

I am trying to implement a stripe checkout process in one of my express.js routes. To do this, I have:
Official Node.js Stripe module
Official client-side Stripe module
A json logger I use to log things like javascript errors, incoming requests and responses from external services like stripe, mongodb, etc…
An Order model defined using mongoose - a MongoDB ODM
My steps are as follows:
Client:
Submit order details which include a stripe payment token
Server:
Create an unpaid order and save to database (order.status is created)
Use stripe client to charge user's credit/debit card
Update order and save to database (order.status is accepted or failed depending on response from Stripe)
Question: If payment is successful after step 2 but an error occurs updating the order in step 3 (due to database server error, outage or similar), what are some appropriate ways to handle this failure scenario and potentially recover from it?
With payment systems, you always need a consolidation process (hourly, daily, monthly) based on sane accounting principles that will check that every money flow is matched.
In your case, I suggest that every external async call logs the sent parameters and the received response. If you do not have a response within a certain time, you know that something has gone wrong on the external system (Stripe, in your case) or on the way back from the external system (you mention a database failure on your side)
Basically, for each async "transaction" that you spawn, you know when you start it and have to decide of a reasonable amount of time before it ends. Thus you have an expected_end_ts in the database.
If you have not received an answer after expected_end_ts, you know that something is wrong. Then you could ask for the status to Stripe or another PSP. Hopefully the API will give you a sane answer as to whether the payment went through or not.
Also note that you should add a step between 1. and 2 : re-read the database. You want to make sure that every payment request you make is really in the database, stored exactly as you are going to send it.

Categories