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

Find existing refunds by refund ID when processing webhook #2581

Merged
merged 3 commits into from
Jun 16, 2023

Conversation

tc33
Copy link
Contributor

@tc33 tc33 commented Mar 22, 2023

Fixes #2580

Changes proposed in this Pull Request:

When handling a refund webhook, this attempts to lookup any existing refund by the refund ID first, before falling back to the current approach using the charge ID. As described in #2580, this will allow other extensions to decouple refunds from the original order the charge is against. The current approach leads to duplicate refunds on the WooCommerce side in these situations.

In general it seems to be more intuitive and more reliable to lookup the refund directly by the refund ID like this, rather than going via the order with the same charge ID.

Testing instructions

The basic behaviour to be tested:

  • Refunds initiated from WooCommerce result in webhook, which bails before creating a new refund, due to the presence of the existing refund
  • Refunds initiated from Stripe admin result in webhook, which creates a new refund within WooCommerce

In addition, to test the scenario I'm hoping to support:

  • When two orders are given the same charge ID (for testing this can just be done directly in the database), refunds initiated from one order in WooCommerce do not add a duplicate refund on the other order

The basic behaviour should not be changed so existing e2e tests for refunds should continue to be relevant. As far as I can tell there's nothing to specifically test for duplicate refunds though.


  • 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

@tc33
Copy link
Contributor Author

tc33 commented Mar 22, 2023

@reykjalin PR as discussed on the Woo Marketplace Slack

public static function get_order_by_refund_id( $refund_id ) {
global $wpdb;

if ( class_exists( 'Automattic\WooCommerce\Utilities\OrderUtil' ) && OrderUtil::custom_orders_table_usage_is_enabled() ) {
Copy link
Contributor

Choose a reason for hiding this comment

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

👏

Copy link
Contributor

@asumaran asumaran left a comment

Choose a reason for hiding this comment

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

Looks good. I haven't tested with Split Orders. I just checked the changes don't break our extension.

Tested the following scenarios:

  • Full refund.
  • Multiple refunds.
  • Disputes.
  • Full refund with HPOS enabled.
  • Multiple refunds with HPOS enabled.
  • Disputes with HPOS enabled.

I only left a non-blocking comment. Please update the PR with the latest develop.

);
$order_id = current( $orders ) ? current( $orders )->get_id() : false;
} else {
$order_id = $wpdb->get_var( $wpdb->prepare( "SELECT DISTINCT ID FROM $wpdb->posts as posts LEFT JOIN $wpdb->postmeta as meta ON posts.ID = meta.post_id WHERE meta.meta_value = %s AND meta.meta_key = %s", $refund_id, '_stripe_refund_id' ) );
Copy link
Contributor

Choose a reason for hiding this comment

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

wc_get_orders() seems to work here even with HPOS enabled. I assume you reused it from get_order_by_charge_id(). It might be better to use wc_get_orders() only if it works in both cases.

Copy link
Contributor

Choose a reason for hiding this comment

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

If you want to test yourself, to enable HPOS you can go to WooCommerce -> Settings -> Advanced -> "Enable the high performance order storage feature"

Copy link
Contributor Author

@tc33 tc33 Jun 15, 2023

Choose a reason for hiding this comment

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

With HPOS disabled, unfortunately the meta_query is just ignored by wc_get_orders().

I did check this and I thought you were right for a moment, because it seems to return the correct order. But, if you test with the limit removed you can see actually all orders are being returned in date order, and naturally in testing it was normally the most recent order being tested with.

This implementation is consistent with get_order_by_intent_id() and get_order_by_setup_intent_id(), which would presumably have the same problem without the custom queries. get_order_by_charge_id() is a little different because it doesn't need a meta query.

Copy link
Contributor

Choose a reason for hiding this comment

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

I see, it's always returning the last order (refund). Which is a coincidence because the last refund is not necessarily the one we are handling on the incoming Webhook.

@@ -1,6 +1,7 @@
*** Changelog ***

= x.x.x - 2023-xx-xx =
* Fix - Lookup existing refunds by refund ID when processing webhooks.
Copy link
Contributor

Choose a reason for hiding this comment

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

Please update your branch with develop, this will likely go in 7.5.0

@tc33 tc33 force-pushed the fix/2580-refund-webhook-id branch from ca3866c to 78500ac Compare June 15, 2023 17:05
@tc33
Copy link
Contributor Author

tc33 commented Jun 15, 2023

Sorry @asumaran for the hold-up. When testing with HPOS I was seeing some odd behaviour. I've now realised that the _stripe_refund_id meta data is not getting saved at all when HPOS is enabled. That looks like a bug, so I'll raise it as a separate issue, I don't think it needs to hold this up any further.

As an aside, I can confirm this PR resolves the issue in Split Orders.

Copy link
Contributor

@asumaran asumaran left a comment

Choose a reason for hiding this comment

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

LGTM. Thanks for your contribution @tc33!

I've now realised that the _stripe_refund_id meta data is not getting saved at all when HPOS is enabled. That looks like a bug, so I'll raise it as a separate issue

Great! Please add more details to replicate the issue.

@asumaran asumaran merged commit b07d999 into woocommerce:develop Jun 16, 2023
@asumaran asumaran self-assigned this Jun 16, 2023
stoyan0v pushed a commit to SiteGround/woocommerce-gateway-stripe that referenced this pull request Oct 3, 2023
@danielvonmitschke
Copy link

As I'm looking through this code (to add these changes to an old version of this plugin) I am wondering if the whole _stripe_refund_id handling is correct.
From what I can see in the code we are only saving one _stripe_refund_id per order.
If I make another refund the previous refund id is overwritten and therefore the get_order_by_refund_id() method cannot work correctly.

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.

Find matching refunds by refund ID
3 participants