woocommerce / woocommerce-gateway-stripe

The official Stripe Payment Gateway for WooCommerce
https://wordpress.org/plugins/woocommerce-gateway-stripe/
236 stars 206 forks source link

Prevent Payment Intents being passed to `process_response()` and marking orders as on-hold with an irrelevant order note #3483

Open mattallan opened 1 month ago

mattallan commented 1 month ago

Fixes #2404

Changes proposed in this Pull Request:

I haven't been able to reproduce this issue on the latest Stripe changes, however merchants are still reporting that some of their orders are being incorrectly marked as on-hold with the following order notes:

image

In trying to understand how this could be possible, I found 2 areas of our code where we could unintentionally pass a Payment Intent object to our process_response() function. Because Payment Intent objects do not have a captured param, that will cause this condition to not pass, resulting in the order being marked as on-hold with an irrelevant order note, then though the payment has been successful (see code here)

Testing instructions

[!NOTE] I've tried many things to reproduce this issue naturally but I could not, so please excuse the hackery in these testing instructions šŸ˜„

  1. Make sure you have legacy checkout experience disabled and the "Issue an authorization on checkout, and capture later" setting also disabled.
  2. Inside process_subscription_payment() tweak this code slightly and manually set $latest_charge to null, to force the intent response to be used instead:
// Use the last charge within the intent or the full response body in case of SEPA.
$latest_charge = null;
$this->process_response( ( ! empty( $latest_charge ) ) ? $latest_charge : $response, $renewal_order );
  1. While on trunk, process a subscription renewal order
  2. Note the subscription is on-hold and the latest renewal order is on-hold
  3. View the renewal order and confirm the "Stripe charge authorized" order note is present.
  4. Switch to this branch and purchase a new subscription or reactivate your existing subscription.
  5. With $latest_charge set to null, the subscription will remain on-hold until the payment_intent.succeeded webhook comes in and processes the renewal payment via the webhook handler and sets the subscription back to active.

Post merge