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

Fix duplicate order notes/emails on subscription renewal orders #3572

Merged

Conversation

mattallan
Copy link
Contributor

@mattallan mattallan commented Nov 4, 2024

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:

1. process_subscription_payment( $amount, $renewal_order )
2. lock_order_payment( $renewal_order )
3. create_and_confirm_intent_for_off_session( ... )
4. unlock_order_payment( $renewal_order )
5. get_latest_charge_from_intent()                // sends a "GET charges/ID" request off to Stripe
6. process_response( $charge, $renewal_order )    // marks the order from pending -> processing/completed status

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 calling process_response() (here). While it seems hacky, this change simulates a slow Stripe request to fetch a charge object and assists with giving time for the payment_intent.succeeded webhook to be sent

$latest_charge = $this->get_latest_charge_from_intent( $response );
sleep( 2 );
$this->process_response( ( ! empty( $latest_charge ) ) ? $latest_charge : $response, $renewal_order );
  1. Activate WooCommerce Subscriptions and purchase a subscription product with any successful card 4242424242424242
  2. While on develop process a renewal order using the "Process renewal" action on the Edit Subscription page
  3. While visiting admin Edit Order page for new renewal order and the admin Edit Subscription page, notice the double order notes.
  4. Checkout this branch and add the same sleep( 2 ) in the code.
  5. Confirm no duplicate order notes.
develop this PR
image image

  • Covered with tests (or have a good reason not to test in description ☝️)
  • Added changelog entry in both changelog.txt and readme.txt (or does not apply)
  • Tested on mobile (or does not apply)

Post merge

@mattallan mattallan requested review from a team and wjrosa and removed request for a team November 4, 2024 03:52
Copy link
Contributor

@wjrosa wjrosa left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for fixing this issue, Matt! Code is good and it works as expected:
Screenshot 2024-11-04 at 19 17 01

@mattallan mattallan merged commit 2c6cab0 into develop Nov 6, 2024
34 of 35 checks passed
@mattallan mattallan deleted the fix/3568-duplicate-order-notes-subscription-renewals branch November 6, 2024 07:09
mattallan added a commit that referenced this pull request Nov 7, 2024
* Unlock the subscription renewal payment after finishing processing the request

* Unlock the order if a failure occurred creating the intent

* add changelog entry
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
2 participants