Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lock Pay::Customer while creating a Stripe customer #1027

Conversation

mpvosseller
Copy link
Contributor

@mpvosseller mpvosseller commented Jul 29, 2024

Pull Request

Summary:

Lock the Pay::Customer while creating its associated Stripe customer. This is to prevent a race condition where more than one Stripe customers can be created for a single Pay::Customer.

Related Issue:

See discussion: #857

Description:

We have twice now encountered a race condition where more than one Stripe customer was created for a single Pay::Customer. This was discovered because of bugs in our React frontend which caused it to accidentally fire multiple API requests to the backend at the same time. Two requests (two threads) would enter the customer method at the same time. Both would see that no Stripe customer yet existed and both would begin creating one. The last one to complete would overwrite the previous one in the database. This was especially bad in our app because we use Stripe checkout. In our case our user ended up checking out with a Stripe customer that was no longer associated with their Pay::Customer. With this change we now lock the Pay::Customer while creating a Stripe customer to prevent more than one from being created.

The change simply updates the customer method by moving the existing code inside a top level with_lock block on the Pay::Customer.

Testing:

I didn't add any new tests as I didn't see an existing pattern for verifying this type of locking code (which is difficult) or an existing tests for similar locking code. I did some manual testing though (using log statements and sleep statements) to verify that only one thread will run the critical section at a time.

Screenshots (if applicable):

Checklist:

  • Code follows the project's coding standards
  • Tests have been added or updated to cover the changes
  • Documentation has been updated (if applicable)
  • All existing tests pass
  • Conforms to the contributing guidelines

Additional Notes:

@mpvosseller mpvosseller changed the title Lock pay customer while creating Stripe customer Lock Pay::Customer while creating a Stripe customer Jul 29, 2024
@mpvosseller mpvosseller marked this pull request as ready for review July 29, 2024 19:16
@excid3
Copy link
Collaborator

excid3 commented Aug 1, 2024

This would only protect in the case that you already have a Pay::Customer record in the database, not the situation where it's lazy created, right?

Actually, I think this'll work fine. If you call set_payment_processor :stripe, it'll create the Pay::Customer record so you'll be able to lock it before creating the Stripe customer. Should handle most all cases. 👍

@excid3 excid3 merged commit ee04c88 into pay-rails:main Aug 1, 2024
36 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants