Handling Stripe Checkout flow and Firestore update, without a race condition? - google-cloud-firestore

I have a plan as to how I would go about doing this and wanted to know if I am headed in the right direction or if there is a better way altogether.
From Firestore Documentation:
In the case of a concurrent edit, Cloud Firestore runs the entire transaction again. For example, if a transaction reads documents and another client modifies any of those documents, Cloud Firestore retries the transaction. This feature ensures that the transaction runs on up-to-date and consistent data.
I have a react front end and node express backend.
Front end sends request to create a checkout session.
In the backend I simply parse the request with the required data then send it to Stripe and receive Stripe's response.
Then for each product P in the customers cart:
We create a Firebase transaction in which:
- get (P) from Firestore
- if P.stock >= 1 then decrement by one and send the redirect uri from stripe to frontend
- else Item is out of stock send custom Failed payment response to front end
Now if two people are trying to purchase the same item and we only have 1 of that item in stock, only one person will be able to complete the payment flow. Is that correct?
I am adding this image to better explain what the flow would look like:
Flow Diagram

To avoid such race condition you must trigger events earlier at a point that will take more time for your customer to start the purchase process than the longest API call scenario.
If you have a basket system, you could simply decrement the stock as soon as the item is added to it. Then when your basket is deleted after the purchase do nothing to the stock else after you make the basket expire after a certain time increment back your stock.
You can still create such a basket-like system even without the user seeing it in the front end. When the purchase succeeds, retrieve your basket doc id from the session metadata and delete it. If you don't delete it after a payment succeeds, that is, before expiration, increment your stock back.
I hope my answer is not too confusing, share the implementation you're heading for, I curious!

Related

PayPal Subscriptions: validating success on a server

I apologize in advance for this question, as its probably rather silly, but I really can not find any answers elsewhere.
My current goal is to integrate a subscription to my website, that allows for a user to have access to certain parts of the site.
I've watched a few videos on it, but it leaves me open ended with some questions.
PayPal allows you to manually create a subscription plan at https://www.paypal.com/billing/plans. When you create a plan here, it gives you the code you need to get the pay pal sub buttons to display on the page. Users can then use them to create the subscription. This is great. However, I have found now way to validate the results of the transaction. My goal would be that if the user was successful, I would be able to retrieve data via the API, that says it was successful, and I could then store than information in my database, and grant them access. Does anyone know how to get the data from the manually created facebook plans?
I did watch a video that had you do everything from your website (you create it all within your own code), the end result however sent everything through $_GET variables in the url. I feel like having this data exposed could result in the manipulation of the subscription, and grant it to users who were unsuccessful in creating a subscription.
I have no problem doing all of the coding to make this work, but I really want to make sure I do it the best possible way.
There is a few way to do it. By setting up a webhook and listen to paypal success notification and react to it. By using the Paypal Subscription API. Or a combination of both.
As you are using the paypal button you will have the onApprove function which will trigger upon user confirming the subscription. You can query the subscription data with:
onApprove={async (data: any, actions: any) => {
const detailedSubscriptionData = await actions.subscription.get()
// activate subscription on your side by sending it to your backend
...
// etc.
}}
Inside the data you have queried above, there will be a paypal transaction id (I-....) that is unique for each transaction, as well as your product id (plan_id). You can now do the following for verification on your backend:
Check if there is already a subscription (from another customer) on your side with that paypal transaction id
Check for the plan_id
Queried the API Subscription Details with the paypal transaction id and check for status, etc. as well as matching of plan_id
Keep in mind that for using the API you will have to queried for an access-token which in turn required you to have Developer App Setup following these steps.

How can I get Cart Details Options in Paypal Sync?

I'm calling this API: https://api.paypal.com/v1/reporting/transactions?start_date=2019-10-01T00:00:00-0700&end_date=2019-10-30T23:59:59-0700&fields=all, and it is returning all transactions but I want to retrieve cart details options although in the fields I have set it to all.
I have read https://developer.paypal.com/docs/integration/direct/sync/#list-transactions.
What I want is the following (but via API):
As a general rule, using PayPal APIs for reporting is a bad idea, for various reasons -- including slowness. But even if they were good for this use case, I know of no way to get the shopping cart data you mention in a list via an API. The only possible solution might be to first get the list without shopping cart data and then query each transaction individually (one API cal per transaction) to get its shopping cart details, but this is incredibly inefficient.
So basically, you should:
Record and save all shopping cart details at transaction time, in your own database, using IPN/webhooks as a listener if necessary
Manually use the activity download of your screenshot to download a CSV, if it's ever necessary to backfill information from the past.
Not use reporting APIs.

Suggestions about how to handle recurring payments when packages involved

Please suggest the following:
when a user want a certain package in my site, let's call it basic package.. his starting his recurring payment..after half a month he canceles the package, but his still paid for a full month, my question is this:
When recurring payment involved, what kind of fields should i save from the ipn, and how should i know exactly when to stop the features of the package a user bought...
I've seen in the ipn, i have 'next recurring payment' date, should i save it and use it? should i, every time a user do anything related to his package,check if the package limit time just ended?
What can be a most preferred solution for this?
When recurring payment involved, what kind of fields should i save from the ipn
All of them, for any kind of IPN whatsoever. Log the entire transaction every time.
and how should i know exactly when to stop the features of the package a user bought...
You should definitely stop when you get a cancel or eot IPN message. You probably don't want to deny service just because of payment difficulties. See below for the rest of it.
I've seen in the ipn, i have 'next recurring payment' date, should i save it and use it?
Save everything. Then you don't have a problem.
should i, every time a user do anything related to his package, check if the package limit time just ended? What can be a most preferred solution for this?
I have a 'subscriptions' table which shows the user/product pair and the expiry date. Every time he pays the expiry date is pushed out another period, or maybe I create it with the final expiry date, I don't remember offhand. When he logs in, he is given the roles associated with all his subscriptions that haven't expired yet.
If he tries to do something he can no longer do due to an expiry:
he isn't even provided with a link in the first place by the webapp, so he can't get there by any direct action
in any other case, e.g. somewhere I forgot to do that, or when he tries to access via a bookmark, container-managed authentication will see that he isn't in the appropriate role and give him a 403.
He can also see a table of his subscriptions showing how long they have left to run, and another table of expired subscriptions.

How to prevent user from withdrawing funds twice?

I have an endpoint in my api, which lets users specify the amount of money they want to withdraw. Before I send the withdrawal request to the payment processor, I check that the requested amount is <= the user's balance. Once the payment is processed, I deduct the amount from the user's balance.
But I'm thinking, someone could send a second request before the first payment is processed, effectively withdrawing the amount twice. How do I prevent this situation?
PS: I'm using Flask Restless and Postgres, if that makes any difference.
In your case, where you're co-ordinating with an external service, it's harder than you'd expect.
Traditional: prepared transactions and XA
The standard solution to this is to use two-phase commit, creating a distributed transaction, where you update the user's record before sending the payment request:
UPDATE account
SET balance = balance - :requested_amount
WHERE balance >= :requested_amount AND user_id = :userid`
If the update succeeds (i.e. they had enough money), you PREPARE TRANSACTION to get the DB to confirm the tx will be saved even if the DB crashes. You then send the request off to the provider, and COMMIT PREPARED or ROLLBACK PREPARED depending on the result.
A lock is held on the balance record by the prepared transaction so no other tx can begin until the prepared tx is rolled back or committed, at which point the new balance is visible.
The old balance shows up to other transactions until the prepared transaction commits or rolls back, unless they use SELECT ... FOR UPDATE or SELECT ... FOR SHARE, in which case they'll wait until the prepared TX commits/rolls back. The NOWAIT option lets them error out instead. It's all very convenient.
However, this approach scales poorly for very large client counts, and it can become problematic if the payment processor is slow or becomes unresponsive. At least in PostgreSQL there's a limit on how many prepared transactions you can have at a time.
More scalable: Keep an open-transaction log in the app
If you don't want to use two-phase commit, you'll need to keep an open transaction log instead.
Rather than just checking the users' balance, the app inserts a row into the active_transactions table as part of beginning a transaction for the user. If the user already has an active transaction, you'll need a unique constraint on active_transactions.user_id so if there are concurrent inserts all but one gets rejected.
You'll probably want to update the user's balance in the same transaction.
Other approaches, like SELECTing to check for the user before inserting a record, are unsafe and prone to race conditions. They're OK if they help provide nicer error messages, etc, but are only acceptable as additional checks.
Then you send the payment request and wait for a response. Whether it's successful or not, when you get a response you delete the open transaction journal entry and copy it to a history table; if the payment failed, you also put the user's balance back up in the same transaction, then commit. Do whatever record-keeping etc you need to in the same transaction you process the payment response in.
Either way: Transaction cleanup
With prepared transactions or an app-defined transaction journal, now you're left with the problem of what to do when your app/server crashes with transactions active and you don't know what the payment processor response for them was ... or whether you actually sent the request yet.
Most payment processor APIs offer some help for this by letting you attach application-defined tokens to each request. If you were using prepared transactions you'd use the prepared transaction Id for this; if you were doing your own transaction journal you'd use the ID you generated when you inserted the entry into your transaction journal. On restart after a crash/restart you can then check each open transaction in your app and ask the payment processor if it knows about it and if so, whether it was successful or not.
You also have to deal with cases where there was no crash, but a payment processor request or response got lost due a transient network issue, etc. You'll need code that periodically checks for apparently abandoned open transactions and re-checks them with the payment processor, like after crash recovery.
There are a number of failure modes you have to deal with:
App crash / network issue / etc after local payment request saved, but before request sent successfully to processor
Processor down/unreachable
Processor sends reply (payment failed / payment OK) but your app is down/unreachable and you never get the response.
App sends payment request then is restarted before payment processor finishes processing the request (or finishes receiving it). Cleanup code thinks the processor never received the request and discards the local transaction record, then payment processor responds to confirm the payment. (There are a few ways to deal with this, but it's really out of scope for this answer.)
... more?
Fun times, eh?
A useful additional sanity check is to periodically (say, daily) fetch the list of transactions from the provider, and compare it to the transactions you thought you did, making sure the completion statuses match. Flag any mismatches for human evaluation. It shouldn't happen, but ....

Payments Callback URL not being call after new local currency changes

After switching to the new Facebook local currency API the process is working, but
my server is not involve in the process so i really don't know if the user bought coins cause the payment callback URL is not being called now (using static payment).
I tried to use real time update to get Facebook data but can make it work, what is the best practice to involve the server in the process so it will be aware of the purchase?
I prefer for security reasons to get the update from Facebook and not from my client.
Ok after searching a log i found the solution
There are two primary methods through which you are notified of the outcome of the purchase, and a further method by which you can verify any payment information.
Firstly, Facebook will return details of the order via a JavaScript callback. The data sent to this callback includes:
payment_id, which uniquely identifies the transaction.
quantity, which indicates the amount of the item which was sold.
request_id, optionally, the developer can provide their own unique identification for the transaction when calling our Javascript SDK to render the payment dialog. This value is then returned upon purchase completion.
status, which indicates the current state of the transaction, i.e. 'pending', 'completed', 'failed' etc.
Secondly, Facebook will issue a realtime update notifying the developer that a new order has completed. The developer can subscribe to the payment_object callback to track order completions, using the payment_id as the unique identity parameter for each transaction.
Thirdly, at any time, the payment_id can be used to verify details of a transaction via the Graph API. Details such as the associated user_id, updated_time and amount can be queried, using the payment_id. The Graph API will also allow you to access further details including any refunds or disputes associated with the transaction.
If for some reason both the JavaScript callback and the realtime update fail and you do not receive the payment_id, we also allow you to query the Graph API using the optional request_id parameter, which can be specified by the developer when invoking the Facebook payment dialog.
please refer to:
https://developers.facebook.com/docs/concepts/payments/
and to:
https://developers.facebook.com/docs/howtos/payments/fulfillment/#rtu