Automattic / woocommerce-payments

Accept payments via credit card. Manage transactions within WordPress.
https://wordpress.org/plugins/woocommerce-payments/
Other
174 stars 69 forks source link

Stale subscription data is used to populate the mandate on PaymentIntent creation during a subscription switch. #5421

Open nirolph opened 1 year ago

nirolph commented 1 year ago

Describe the bug

When the customer switches subscriptions (WooCommerce Subscriptions), the subscription data used to populate the mandate is stale. WCS updates the subscription with the new details (renewal date, billing interval, price) after payment is completed. But the mandate is populated before payment completes, during the PaymentIntent creation. Due to this mismatch, the mandate will always use stale subscription data during a subscription switch.

This has two side effects:

To Reproduce

Initial setup

  1. Set up a shop with WooCommerce Subscriptions and WooCommerce Payments.
  2. Under the WooCommerce Subscriptions settings page enable switching between simple subscription products. Update the other settings as shown in the screenshot.
  3. Create a few simple subscriptions: Free plan ($0/month), Basic plan ($7/month), Pro plan ($12/month)
  4. Create a grouped product and include all the previously created products under Linked products > grouped products. This allows switching between the subscriptions.

Switching from a free subscription to a paid one

  1. "Purchase" the free plan. Notice no charge is made.
  2. Open your new subscription in your customers dashboard (not admin) and click on the Upgrade or Downgrade button. This will take you to the grouped product page where you can select a new plan.
  3. On the plan selection page, opt to upgrade to any of the paid plans.
  4. Attempt to pay for the new plan.
  5. Confirm that payment fails and you see this error in the checkout page: We're not able to process this request. Please refresh the page and try again.
  6. Open the Stripe logs and locate the corresponding POST /v1/payment_intents entry. Here's an example of a log from my test.
  7. Confirm the API call failed with parameter_invalid_integer - payment_method_options[card][mandate_options][amount] This value must be greater than or equal to 1..
  8. Confirm that the payment_method_options.card.mandate_options is populated with the stale (old/free) subscription data.
  9. Please test around this issue.

Switching between paid subscriptions

  1. Purchase the Base plan.
  2. Open the Stripe logs and locate the corresponding POST /v1/payment_intents entry. Here's an example of a log from my test.
  3. Confirm that the correct data is set in payment_method_options.card.mandate_options.
  4. Open your new subscription in your customers dashboard (not admin) and click on the Upgrade or Downgrade button. This will take you to the grouped product page where you can select a new plan.
  5. On the plan selection page, opt to upgrade to any of the paid plans.
  6. Finalize the subscription switch by checking out.
  7. Open the Stripe logs and locate the corresponding POST /v1/payment_intents entry. Here's an example of a log from my test.
  8. Confirm that stale subscription data (from the old subscription) was used to populate payment_method_options.card.mandate_options.
  9. Please test around this issue.

Renewals after a subscription switch.

I tested the following scenarios:

  1. USD store, USD currency, paid with USD card 4000002500003155 (3DS).
    • ✅ Renewal via AS worked before switching subscriptions.
    • ✅ Renewal via As worked after switching subscriptions.
  2. Indian store, INR currency, paid with INR card 4000003560000123 (3DS2). Full disclosure: I wasn't able to test the entire flow, as I couldn't figure out a way to receive the emails to confirm the charge quickly. But I still noticed differences between renewals done before and after switching subscriptions. So please re-test this to confirm.
    • ✅ Renewals via AS worked before switching subscriptions. An on-hold renewal order was created and a subscription was placed on-hold as well. (example Stripe log)
    • 🟥 Renewal via AS didn't work after switching subscriptions. A failed renewal order was created and the subscription was placed on-hold. (example Stripe log) Something similar was reported in #5311

Actual behavior

The mandate is populated using the stale subscription data during a subscription switch.

Screenshots

WCS switching settings

Screenshot 2023-01-18 at 12 43 55

Expected behavior

The mandate uses the correct information during a subscription switch.

Desktop (please complete the following information):

Smartphone (please complete the following information):

Additional context

haszari commented 1 year ago

Marking as high priority. This could be affecting merchants using WCPay gateway and tokenised subscriptions (WC Subscriptions extension), and would be a regression of behaviour – switching from $0 to $paid subscription plan is broken.

Note #5442 may mitigate this:

zmaglica commented 1 year ago

@haszari is this issue on Helix's radar? I am triaging this issue and I at first I wanted to put it in prioritization, but if team Helix is going to pick this up soon, I can move it to the different queue. Feel free to move it into "Ready for prioritization queue if this issue is not picked anytime soon by Helix team.

shendy-a8c commented 1 year ago

is this issue on Helix's radar?

It's in our project board and I'm looking for a new issue to work on, so picking this one up.

haszari commented 1 year ago

@shendy-a8c what's the status of this (do you have a draft PR/branch)? Any blockers – is it feasible for us to ship a fix in next sprint?

thisissandip commented 1 year ago

Just noting here that a similar issue is now merged within WooCommerce Stripe: https://github.com/woocommerce/woocommerce-gateway-stripe/pull/2561

thisissandip commented 1 year ago

cc @imodouglas