Retrieve Stripe charge status from used source token - Node + MongoDB - javascript

Let's say my user submits a credit card token to my server to complete a purchase. Then I save this token in my user's cart, and in the sequence, I charge it.
I receive a success response with the charge id and, suddenly, my server stops working just before I can store this charge id in my user's cart.
The charge was definitely made, but I couldn't save it in my database to generate the user's order and make the products available, so I don't know if it succeeded or not.
What I want to do is to mitigate any possibility of duplicate payments. I've looked into the entire Stripe's API but I just couldn't find any information about if it's possible to retrieve a charge using the card token that was used to create it.
Every time when my server reboots, some processes are triggered to verify database integrity (I'm using MongoDB, by the way), and one of those processes tries to make charges for all user carts (with already generated card tokens) that were left in a "waiting payment" state. If a card token wasn't charged yet, everything works well, but if it was charged already, I just don't know if the charge succeeded or not, so I can't update my cart to a consistent state.
I can retrieve the card token by using "stripe.tokens.retrieve(token).then(...", but I just know if it was used or not. I have to find a way to discover if it was charged and if it succeeded.
I've tried to store the card token as a metadata of my charge, so I'd be able to retrieve charges that contain this card token as metadata, but it seems that this kind of query is not possible.
Does anyone know how to overcome this issue or any better logic to grant payment consistency in a MongoDB payment implementation?

As #karllekko commented, this is the answer to my question:
"You can generate and save a unique ID at the beginning of the cart checkout attempt, and use this as an idempotency key when making the charge request : stripe.com/docs/api#idempotent_requests Then if you have a crash, you can safely retry the request, and if you use the same key, it won't create a duplicate charge(you just get the original response back)"
And following my logic, I'm making those failed payments attempts to be executed again without the need of the user to retry it manually, so I'm storing the card-token generated client-side in my user's cart and in the sequence I'm charging this same token and using it as the idempotency key to avoid duplicate payments, as they are unique within Stripe's DB.
This is the request that avoids duplicate payments and always gives me the same response, successful or not, for the same card-token, and makes it possible to grant a consistent state to my user's cart:
stripe.charges.create(
{
amount: amount,
currency: 'usd',
source: token
},
{
idempotency_key: token
}
).then(
res => resolve(res),// Proceed to a success card/order state and allow products/services to be accessed by user. If an attempt of make duplicate payments using the same card-token happens, you'll receive the same success response and will be able to proceed to the same success card/order state.
err => reject({e: err, m: 'PAYMENT_ERROR', i: 'STRIPE_CHARGE_ERROR'})// An error occurred with the original charge request, so you'll have to rollback to the pre-checkout cart state. Even if you couldn't get the original error response, using an idempotency key will give you the exactly same error response in this step when you retry the charge attempt for this same card-token, so you know you have to rollback.
)
Thanks, #karllekko!

Related

How to Capture paypal_fee in Paypal Smart Button/JS SDK (Laravel 8)

I want to retrieve the paypal_fee when paying an order using PayPal smart buttons/ Javascript SDK .
PayPal Smart Payment Button
This is to compute the order net income before saving it in my database. I want to know how to retrieve the value of paypal_fee in every order and store it in a variable.
The JS SDK actions.order.create / actions.order.capture are for very simple use cases. If you are going to do anything automated with the order information after capture, including saving it to a database, create and capture the order from a server.
Use the v2/checkout/orders API and make two routes (url paths) on your server, one for 'Create Order' and one for 'Capture Order'. You could use one of the (recently deprecated) Checkout-*-SDKs for the routes' API calls to PayPal, or your own HTTPS implementation of first getting an access token and then doing the call. Both of these routes should return/output only JSON data (no HTML or text). Inside the 2nd route, when the capture API is successful you should verify the amount was correct and store its resulting payment details in your database (particularly purchase_units[0].payments.captures[0].id, which is the PayPal transaction ID) and perform any necessary business logic (such as reserving product or sending an email) immediately before forwarding return JSON to the frontend caller. In the event of an error forward the JSON details of it as well, since the frontend must handle such cases.
Pair those 2 routes with this frontend approval flow: https://developer.paypal.com/demo/checkout/#/pattern/server . (If you need to send any additional data from the client to the server, such as an items array or selected options, add a body parameter to the fetch with a value that is a JSON string or object)

Stripe - Refund Checkout Session

I’m trying to implement a method for my connected account owners to be able to refund charges collected via a Stripe Checkout Session.
In order to do so, I want to use the following code:
const refund = await stripe.refunds.create({
payment_intent: pi_id,
});
In order to obtain the payment_intent_id, I am listening to the checkout.session.completed webhook, and accessing data.object.payment_intent.
When I attempt to create a refund using this payment intent id, I get the No such paymentintent error from Stripe. I noticed in my Stripe test account that there is a charge ID associated with the payment, so maybe I should be using that - but I’m not sure how to retrieve that programmatically from the checkout session.
You're making the API call correctly.
“No such...” errors with Stripe are usually caused by either a mismatch in API keys (e.g. using a mixture of your test plus live keys) or by trying to access objects that exist on a different account (e.g. trying to perform an operation from your platform account on an object that was created on a connected account).

PayPal Checkout: Is it safe to receive a payment with only client-side code?

I'm using the PayPal API to put payment options to my website. In the tutorial they have, they are rendering the button and setting up the transaction entirely at the client side with JavaScript. Here is the sample code:
<script>
paypal.Buttons({
createOrder: function(data, actions) {
// This function sets up the details of the transaction, including the amount and line item details.
return actions.order.create({
purchase_units: [{
amount: {
value: '0.01'
}
}]
});
},
onApprove: function(data, actions) {
// This function captures the funds from the transaction.
return actions.order.capture().then(function(details) {
// This function shows a transaction success message to your buyer.
alert('Transaction completed by ' + details.payer.name.given_name);
});
}
}).render('#paypal-button-container');
//This function displays Smart Payment Buttons on your web page.
</script>
Is this secure?
The user can just change the payment amount in the code on his side and pay less. Even if I set up client-side code to send transaction-id once the transaction is successful (ie. make a POST request at onApprove), so that I can have a server-side code check if the amount sent is correct, the client can still change the code on his side to send a fake transaction-id.
I basically need a mechanism to check if I definitely received the right amount, before delivering the product. I obviously need to make this check at the server-side but I can't figure out a secure way to do it because I need to get some info from the client-side which might be fake. How do I prevent the user from pretending to have paid for example by sending a past transaction-id?
You are correct that the user can always change the amount in client-side code, and send a payment for a lower amount. That's how client side payments work.
Any logic to keep track of which payments are real and for the correct amount must be on your server.
For PayPal Checkout, here's the front-end UI you should use: https://developer.paypal.com/demo/checkout/#/pattern/server
You'll need two corresponding routes on your server, one for 'Create Order' and one for 'Capture Order'. You can use one of the Checkout-lanuagename-SDK's (edit:though these have been deprecated now) for the routes' API calls to PayPal, or your own HTTPS implementation of first getting an access token and then doing the call (there is a full-stack example in node.js at the main PayPal Checkout guide, but it can be done with any backend server language).
Both of the two routes should return only JSON data (no HTML or text). Inside the 2nd route, when the capture API is successful you should verify the amount was correct and store its resulting payment details in your database (particularly purchase_units[0].payments.captures[0].id, which is the PayPal transaction ID) and perform any necessary business logic (such as sending confirmation emails or reserving product) immediately before forwarding your return JSON to the frontend caller. In the event of an error forward the JSON details of it as well, since the frontend must handle such cases.
I'm sorry but this still all seems to be very insecure to me. Once you have given your client-ID in the paypal javascript tag, then any good hacker can use devtools in Google or Firefox, etc. and replace your paypal.buttons code with their own paypal.buttons code and happily send the transaction along to paypal without using your client-side code or server code.
I did some test of this theory and in five minutes or less I was able to redo the code in the client javascript and pay whatever I wanted for the product I was buying (this in a test environment).
What am I missing here that doesn't render the client side code totally insecure.

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

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.

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