Skip to content

Refactor WC_Stripe_Subscriptions_Trait->maybe_render_subscription_payment_method() #4568

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

Open
wants to merge 22 commits into
base: develop
Choose a base branch
from

Conversation

daledupreez
Copy link
Contributor

@daledupreez daledupreez commented Aug 8, 2025

Changes proposed in this Pull Request:

This PR refactors the code in WC_Stripe_Subscriptions_Trait->maybe_render_subscription_payment_method(). There are a few aspects to the refactor that are worth capturing in more detail:

  • We now fetch only the specific payment method saved with the current subscription via the internal WC_Stripe_API::get_payment_method() API so we can handle older src_ IDs.
  • We use a static variable cache to avoid unnecessary API calls within the same request, as I saw two API requests triggered when viewing the Subscription details page
  • The logic to translate the payment method details to a string has been extracted into a helper function that is simpler than what we had.
  • I've also expanded the unit tests to cover more payment method types

Open questions

  • I am not sure how/if we need to handle older src_* sources. I don't know how Stripe handles those via the payment_methods APIs. As per this suggestion from @Mayisha, I've switched to calling WC_Stripe_API::get_payment_method() and then check the customer ID afterwards.
  • I don't know if it's worth using a transient for the payment method caching, or whether we should just cache the payment method without the customer ID dimension. I think both should be unique.

Testing instructions

  • Set up a store with WooCommerce Subscriptions installed and Stripe is connected
  • Ensure you have multiple payment methods that support recurring payments enabled
  • Ensure that you have a plugin like Debug Bar installed or that you have logging enabled for the Stripe plugin
  • While logged in, purchase a subscription
  • While running on develop, navigate to My account > Subscriptions > Your just-purchased subscription (or My Subscription if you only have one subscription)
  • Note what is displayed for the "Payment" row (see the screenshot below)
  • Check your logs and verify that you see multiple API calls to the Stripe /v1/payment_methods API
  • Switch to this branch -- git checkout refactor/subscriptions-trait-payment-method-rendering
  • Reload the subscription page
  • Verify that you have the same value shown for the "Payment" element
  • Verify that only one API call was made to the Stripe /v1/payment_methods/:payment_method_id endpoint
  • If you have an older src_* payment method ID, it would be good to test that, too!
Screenshot 2025-08-15 at 12 46 09
  • Covered with tests (or have a good reason not to test in description ☝️)
  • [N/A] Tested on mobile (or does not apply)

Changelog entry

  • This Pull Request does not require a changelog entry. (Comment required below)
Changelog Entry Comment

Comment

Post merge

@daledupreez daledupreez requested a review from Copilot August 8, 2025 15:33
Copilot

This comment was marked as outdated.

Base automatically changed from refactor/fetching-all-customer-payment-methods to develop August 11, 2025 14:52
@daledupreez daledupreez requested a review from Copilot August 15, 2025 10:34
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR refactors the subscription payment method rendering logic to reduce unnecessary Stripe API calls. Instead of iterating through all payment method types and making multiple API calls to find a matching payment method, it now makes a single direct API call to retrieve the specific payment method details.

  • Replaces loop-based payment method retrieval with direct API call using customer ID and payment method ID
  • Extracts payment method display logic into separate helper methods for better maintainability
  • Adds comprehensive test coverage for various payment method types

Reviewed Changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

File Description
includes/compat/trait-wc-stripe-subscriptions.php Refactors maybe_render_subscription_payment_method() to use direct API calls and extracts display logic into helper methods
tests/phpunit/WC_Stripe_Payment_Gateway_Test.php Replaces simple test with comprehensive data provider covering multiple payment method types
readme.txt Adds changelog entry for the optimization
changelog.txt Adds changelog entry for the optimization

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

}

if ( isset( $cached_payment_methods[ $stripe_customer_id ][ $payment_method_id ] ) ) {
return $cached_payment_methods[ $stripe_customer_id ][ $payment_method_id ];
Copy link
Preview

Copilot AI Aug 15, 2025

Choose a reason for hiding this comment

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

Using a static variable for caching in a web environment can lead to data leakage between requests or users. Consider using WordPress transients or object cache instead for safer caching that respects request boundaries.

Suggested change
return $cached_payment_methods[ $stripe_customer_id ][ $payment_method_id ];
if ( empty( $stripe_customer_id ) || empty( $payment_method_id ) ) {
return null;
}
$cache_key = 'stripe_sub_pm_' . md5( $stripe_customer_id . '|' . $payment_method_id );
$cache_group = 'stripe_payment_methods';
$saved_payment_method = wp_cache_get( $cache_key, $cache_group );
if ( false !== $saved_payment_method ) {
return $saved_payment_method;

Copilot uses AI. Check for mistakes.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

For this use case, I think it's OK to use a static variable, due to us using the customer ID and payment method ID in the path, but I'd be happy to switch to a transient.

@daledupreez daledupreez marked this pull request as ready for review August 15, 2025 10:54
@daledupreez daledupreez requested review from a team, diegocurbelo and Mayisha and removed request for a team August 15, 2025 10:54
@daledupreez daledupreez self-assigned this Aug 15, 2025
Copy link
Contributor

@Mayisha Mayisha left a comment

Choose a reason for hiding this comment

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

Functionally it is working as described. I see only one request to customers/cus_abc/payment_methods/pm_xyz in my logs 🎉

/**
* Helper function to get the descriptive text for a payment method or source.
*
* @param object $source The payment method or source object.
Copy link
Contributor

Choose a reason for hiding this comment

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

Optional: My preference would be to call it a payment_method in all the new methods.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good call. Updated in 9e6ebdd


$encoded_customer_id = rawurlencode( $stripe_customer_id );
$encoded_payment_method_id = rawurlencode( $payment_method_id );
$api_path = "customers/{$encoded_customer_id}/payment_methods/{$encoded_payment_method_id}";
Copy link
Contributor

Choose a reason for hiding this comment

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

As an alternate approach which would cover this related to the open question as well,

I am not sure how/if we need to handle older src_* sources. I don't know how Stripe handles those via the payment_methods APIs.

we can retrieve the payment method or source using the get_payment_method method and check if payment_method->customer matches the $stripe_customer_id.

AFAIK, /payment_methods/src_xyz fails so customers/customer_abc/payment_methods/src_xyz might also fail.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good call. I've switched that over in 3aecb6c.

}

/**
* Helper function to get and temporarily cache the payment method details for a customer and payment method ID.
Copy link
Contributor

Choose a reason for hiding this comment

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

Optional: we can move this helper method to WC_Stripe_Subscriptions_Helper.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think this is also a good suggestion. Done in 2920871.

Copy link
Member

@diegocurbelo diegocurbelo left a comment

Choose a reason for hiding this comment

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

The code changes look good, and it tests well 🚢

Screenshot 2025-08-18 at 11 26 23

I don't know if it's worth using a transient for the payment method caching, or whether we should just cache the payment method without the customer ID dimension. I think both should be unique.

I like the transient idea, I think is consistent with how we are caching other things.. but we cal also improve this later, after we add a auto-cleanup mechanism to the DB cache.

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.

3 participants