Fix duplicate order notes/emails on subscription renewal orders #3572
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Fixes #3568
Part of the on-going duplicate order notes/emails issue: #3501
Changes proposed in this Pull Request:
During the subscription renewal order payment process, the order payment is locked before initiating the first Stripe create/confirm intent request (code reference).
In the current develop branch, the order is being unlocked prematurely (code reference). Here's the current sequence:
When there are delays in fetching the latest charge from Stripe or if the
payment_intent.succeeded
webhook is sent quickly, the current request and incoming webhook may be processed in parallel, leading to both requests calling$renewal_order->payment_complete()
and adding the "Stripe charge completed." order note.This results in duplicate emails being sent to customers and third-party code hooked to status transitions is fired multiple times.
Solution
This PR addresses the issue by moving the unlock step to occur after processing the response and handling any exceptions. This ensures the order remains locked until all necessary actions are complete, preventing parallel handling conflicts.
Testing instructions
To replicate this consistently, I had to add a small
sleep( 2 );
between fetching the latest charge from Stripe and callingprocess_response()
(here). While it seems hacky, this change simulates a slow Stripe request to fetch a charge object and assists with giving time for thepayment_intent.succeeded
webhook to be sent4242424242424242
develop
process a renewal order using the "Process renewal" action on the Edit Subscription pagesleep( 2 )
in the code.develop
changelog.txt
andreadme.txt
(or does not apply)Post merge