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

Moving the method checking for the subscription extension status to a new helper class #3864

Open
wants to merge 20 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
a0815de
Adding admin notices for detached subscriptions
wjrosa Feb 10, 2025
762c128
Moving the method checking for the subscription extension status to a…
wjrosa Feb 10, 2025
ddd4ed4
Making use of the new method in the admin notices class
wjrosa Feb 10, 2025
0b15477
New helper method to retrieve detached subscriptions
wjrosa Feb 10, 2025
afb9de8
Merge branch 'develop' into dev/new-subscriptions-helper-class
wjrosa Feb 13, 2025
0af77fc
Changelog and readme entries
wjrosa Feb 13, 2025
10bc5e5
Merge branch 'develop' into dev/new-subscriptions-helper-class
wjrosa Feb 13, 2025
b443222
Making use of the new retrieval method in the helper class
wjrosa Feb 13, 2025
d15a81f
Adding specific unit tests
wjrosa Feb 13, 2025
6e86abc
Cached test
wjrosa Feb 13, 2025
0c2a450
Merge branch 'develop' into dev/new-subscriptions-helper-class
wjrosa Feb 14, 2025
6a6a904
Merge branch 'develop' into dev/new-subscriptions-helper-class
diegocurbelo Feb 17, 2025
e34db24
Merge branch 'develop' into dev/new-subscriptions-helper-class
wjrosa Feb 18, 2025
01390a2
Merge branch 'develop' into dev/new-subscriptions-helper-class
diegocurbelo Feb 19, 2025
7e21981
Merge branch 'develop' into dev/new-subscriptions-helper-class
wjrosa Feb 20, 2025
238b40b
Merge branch 'develop' into dev/new-subscriptions-helper-class
wjrosa Feb 20, 2025
d69880b
Merge branch 'develop' into dev/new-subscriptions-helper-class
wjrosa Feb 25, 2025
e3bb49c
Merge branch 'develop' into dev/new-subscriptions-helper-class
wjrosa Feb 26, 2025
d12e6fe
Fix tests
wjrosa Feb 26, 2025
fabe032
Merge branch 'develop' into dev/new-subscriptions-helper-class
diegocurbelo Feb 26, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
*** Changelog ***

= 9.3.0 - xxxx-xx-xx =
* Dev - Moves the method to check if the subscriptions extension is enabled to a new helper class.
* Fix - Fixes a fatal error that might happen when a payment method ID cannot be retrieved during the processing of an order (new checkout experience).
* Dev - Generates a code coverage report for PHP Unit tests as a comment on PRs.
* Add - Adds Stripe specific information to the System Status Report data.
Expand Down
52 changes: 2 additions & 50 deletions includes/admin/class-wc-stripe-admin-notices.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,6 @@
* @since 4.1.0
*/
class WC_Stripe_Admin_Notices {
/**
* Transient key for detached subscriptions.
*
* @var string
*/
private const DETACHED_SUBSCRIPTIONS_TRANSIENT_KEY = 'wcstripe_detached_subscriptions';

/**
* Stripe customer page base URL.
*
Expand Down Expand Up @@ -73,7 +66,7 @@ public function admin_notices() {
$this->payment_methods_check_environment();

// Subscription related checks.
if ( class_exists( 'WC_Subscriptions' ) && class_exists( 'WC_Subscription' ) && version_compare( WC_Subscriptions::$version, '2.2.0', '>=' ) ) {
if ( WC_Stripe_Subscriptions_Helper::is_subscriptions_enabled() ) {
$this->subscriptions_check_environment();
}

Expand Down Expand Up @@ -405,7 +398,7 @@ public function payment_methods_check_environment() {
*/
public function subscriptions_check_environment() {
$detached_messages = '';
$subscriptions = $this->get_detached_subscriptions();
$subscriptions = WC_Stripe_Subscriptions_Helper::get_detached_subscriptions();
foreach ( $subscriptions as $subscription ) {
$customer_payment_method_link = sprintf(
'<a href="%s">%s</a>',
Expand Down Expand Up @@ -529,47 +522,6 @@ public function stripe_updated() {
update_option( 'wc_stripe_show_sca_notice', 'no' );
}
}

/**
* Returns a list of subscriptions without a payment method attached.
*
* @return array
*/
private function get_detached_subscriptions() {
// Check if we have a cached result.
$cached_subscriptions = get_transient( self::DETACHED_SUBSCRIPTIONS_TRANSIENT_KEY );
if ( ! empty( $cached_subscriptions ) ) {
return $cached_subscriptions;
}

$detached_subscriptions = [];
$subscriptions = wcs_get_subscriptions(
[
'subscriptions_per_page' => -1,
'orderby' => 'date',
'order' => 'DESC',
'subscription_status' => [ 'active', 'on-hold', 'pending-cancel' ],
]
);
foreach ( $subscriptions as $subscription ) {
$source_id = $subscription->get_meta( '_stripe_source_id' );
if ( $source_id ) {
$payment_method = WC_Stripe_API::get_payment_method( $source_id );
if ( ! $payment_method->customer ) {
$detached_subscriptions[] = [
'id' => $subscription->get_id(),
'customer_id' => $subscription->get_meta( '_stripe_customer_id' ),
'change_payment_method_url' => $subscription->get_change_payment_method_url(),
];
}
}
}

// Cache the result for a day.
set_transient( self::DETACHED_SUBSCRIPTIONS_TRANSIENT_KEY, $detached_subscriptions, DAY_IN_SECONDS );

return $detached_subscriptions;
}
}

new WC_Stripe_Admin_Notices();
66 changes: 66 additions & 0 deletions includes/compat/class-wc-stripe-subscriptions-helper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}

/**
* Helper class to handle subscriptions.
*/
class WC_Stripe_Subscriptions_Helper {
/**
* Transient key for detached subscriptions.
*
* @var string
*/
private const DETACHED_SUBSCRIPTIONS_TRANSIENT_KEY = 'wcstripe_detached_subscriptions';

/**
* Checks if subscriptions are enabled on the site.
*
* @return bool Whether subscriptions is enabled or not.
*/
public static function is_subscriptions_enabled() {
return class_exists( 'WC_Subscriptions' ) && class_exists( 'WC_Subscription' ) && version_compare( WC_Subscriptions::$version, '2.2.0', '>=' );
}

/**
* Returns a list of subscriptions that are detached from the customer.
*
* @return array
*/
public static function get_detached_subscriptions() {
// Check if we have a cached result.
$cached_subscriptions = get_transient( self::DETACHED_SUBSCRIPTIONS_TRANSIENT_KEY );
if ( ! empty( $cached_subscriptions ) ) {
return $cached_subscriptions;
}

$detached_subscriptions = [];
$subscriptions = wcs_get_subscriptions(
[
'subscriptions_per_page' => -1,
'orderby' => 'date',
'order' => 'DESC',
'subscription_status' => [ 'active', 'on-hold', 'pending-cancel' ],
]
);
foreach ( $subscriptions as $subscription ) {
$source_id = $subscription->get_meta( '_stripe_source_id' );
if ( $source_id ) {
$payment_method = WC_Stripe_API::get_payment_method( $source_id );
if ( ! $payment_method->customer ) {
$detached_subscriptions[] = [
'id' => $subscription->get_id(),
'customer_id' => $subscription->get_meta( '_stripe_customer_id' ),
'change_payment_method_url' => $subscription->get_change_payment_method_url(),
];
}
}
}

// Cache the result for a day.
set_transient( self::DETACHED_SUBSCRIPTIONS_TRANSIENT_KEY, $detached_subscriptions, DAY_IN_SECONDS );

return $detached_subscriptions;
}
}
9 changes: 6 additions & 3 deletions includes/compat/trait-wc-stripe-subscriptions-utilities.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,12 @@ trait WC_Stripe_Subscriptions_Utilities_Trait {
* @since 5.6.0
*
* @return bool Whether subscriptions is enabled or not.
*
* @deprecated 9.2.0 Use WC_Stripe_Subscriptions_Helper::is_subscriptions_enabled instead.
*/
public function is_subscriptions_enabled() {
return class_exists( 'WC_Subscriptions' ) && class_exists( 'WC_Subscription' ) && version_compare( WC_Subscriptions::$version, '2.2.0', '>=' );
wc_deprecated_function( 'is_subscriptions_enabled', '9.2.0', 'WC_Stripe_Subscriptions_Helper::is_subscriptions_enabled' );
return WC_Stripe_Subscriptions_Helper::is_subscriptions_enabled();
}

/**
Expand Down Expand Up @@ -64,7 +67,7 @@ public function is_changing_payment_method_for_subscription() {
* @return bool
*/
public function is_payment_recurring( $order_id ) {
if ( ! $this->is_subscriptions_enabled() ) {
if ( ! WC_Stripe_Subscriptions_Helper::is_subscriptions_enabled() ) {
return false;
}
return $this->is_changing_payment_method_for_subscription() || $this->has_subscription( $order_id );
Expand Down Expand Up @@ -103,7 +106,7 @@ public function display_save_payment_method_checkbox( $display ) {
* @return bool
*/
public function is_subscription_item_in_cart() {
if ( $this->is_subscriptions_enabled() ) {
if ( WC_Stripe_Subscriptions_Helper::is_subscriptions_enabled() ) {
return ( class_exists( 'WC_Subscriptions_Cart' ) && WC_Subscriptions_Cart::cart_contains_subscription() ) || $this->cart_contains_renewal();
}
return false;
Expand Down
14 changes: 7 additions & 7 deletions includes/compat/trait-wc-stripe-subscriptions.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ trait WC_Stripe_Subscriptions_Trait {
* @since 5.6.0
*/
public function maybe_init_subscriptions() {
if ( ! $this->is_subscriptions_enabled() ) {
if ( ! WC_Stripe_Subscriptions_Helper::is_subscriptions_enabled() ) {
return;
}

Expand Down Expand Up @@ -211,7 +211,7 @@ public function differentiate_change_payment_method_form() {
*/
public function maybe_change_subscription_payment_method( $order_id ) {
return (
$this->is_subscriptions_enabled() &&
WC_Stripe_Subscriptions_Helper::is_subscriptions_enabled() &&
$this->has_subscription( $order_id ) &&
$this->is_changing_payment_method_for_subscription()
);
Expand Down Expand Up @@ -572,7 +572,7 @@ public function process_subscription_payment( $amount, $renewal_order, $retry =
* @param string $payment_gateway_id The payment method ID. eg 'stripe.
*/
public function maybe_update_source_on_subscription_order( $order, $source, $payment_gateway_id = '' ) {
if ( ! $this->is_subscriptions_enabled() ) {
if ( ! WC_Stripe_Subscriptions_Helper::is_subscriptions_enabled() ) {
return;
}

Expand Down Expand Up @@ -1081,7 +1081,7 @@ public function redirect_after_early_renewal( $url ) {
* @param stdClass $intent The Payment Intent object.
*/
protected function maybe_process_subscription_early_renewal_success( $order, $intent ) {
if ( $this->is_subscriptions_enabled() && isset( $_GET['early_renewal'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
if ( WC_Stripe_Subscriptions_Helper::is_subscriptions_enabled() && isset( $_GET['early_renewal'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
if ( function_exists( 'wcs_update_dates_after_early_renewal' ) && function_exists( 'wcs_get_subscription' ) ) {
wcs_update_dates_after_early_renewal( wcs_get_subscription( $order->get_meta( '_subscription_renewal' ) ), $order );
}
Expand All @@ -1098,7 +1098,7 @@ protected function maybe_process_subscription_early_renewal_success( $order, $in
* @param stdClass $intent The Payment Intent object (unused).
*/
protected function maybe_process_subscription_early_renewal_failure( $order, $intent ) {
if ( $this->is_subscriptions_enabled() && isset( $_GET['early_renewal'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
if ( WC_Stripe_Subscriptions_Helper::is_subscriptions_enabled() && isset( $_GET['early_renewal'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
$order->delete( true );
wc_add_notice( __( 'Payment authorization for the renewal order was unsuccessful, please try again.', 'woocommerce-gateway-stripe' ), 'error' );
$renewal_url = ( function_exists( 'wcs_get_early_renewal_url' ) && function_exists( 'wcs_get_subscription' ) )
Expand Down Expand Up @@ -1130,7 +1130,7 @@ protected function must_authorize_off_session( $payment_intent ) {
* @param string $payment_method_type The payment method ID. eg 'stripe', 'stripe_sepa'.
*/
public function update_subscription_payment_method_from_order( $order, $payment_method_type ) {
if ( ! $this->is_subscriptions_enabled() || ! function_exists( 'wcs_get_subscriptions_for_order' ) ) {
if ( ! WC_Stripe_Subscriptions_Helper::is_subscriptions_enabled() || ! function_exists( 'wcs_get_subscriptions_for_order' ) ) {
return;
}

Expand All @@ -1149,7 +1149,7 @@ public function update_subscription_payment_method_from_order( $order, $payment_
*/
public function disable_subscription_edit_for_india( $editable, $order ) {
$parent_order = wc_get_order( $order->get_parent_id() );
if ( $this->is_subscriptions_enabled()
if ( WC_Stripe_Subscriptions_Helper::is_subscriptions_enabled()
&& $this->is_subscription( $order )
&& $parent_order
&& ! empty( $parent_order->get_meta( '_stripe_mandate_id', true ) ) ) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -478,7 +478,7 @@ public function javascript_params() {
$stripe_params['customerData'] = [ 'billing_country' => $order->get_billing_country() ];
}

if ( $this->is_subscriptions_enabled() && $is_change_payment_method ) {
if ( WC_Stripe_Subscriptions_Helper::is_subscriptions_enabled() && $is_change_payment_method ) {
$stripe_params['isChangingPayment'] = true;
$stripe_params['addPaymentReturnURL'] = wp_sanitize_redirect( esc_url_raw( home_url( add_query_arg( [] ) ) ) );

Expand Down
1 change: 1 addition & 0 deletions readme.txt
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ If you get stuck, you can ask for help in the [Plugin Forum](https://wordpress.o
== Changelog ==

= 9.3.0 - xxxx-xx-xx =
* Dev - Moves the method to check if the subscriptions extension is enabled to a new helper class.
* Fix - Fixes a fatal error that might happen when a payment method ID cannot be retrieved during the processing of an order (new checkout experience).
* Dev - Generates a code coverage report for PHP Unit tests as a comment on PRs.
* Add - Adds Stripe specific information to the System Status Report data.
Expand Down
71 changes: 71 additions & 0 deletions tests/phpunit/compat/test-class-wc-stripe-subscriptions-helper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<?php
/**
* Class WC_Stripe_Subscriptions_Helper_Test
*
* @package WooCommerce_Stripe/Tests/WC_Stripe_Subscriptions_Helper
*/

/**
* Class WC_Stripe_Subscriptions_Helper tests.
*/
class WC_Stripe_Subscriptions_Helper_Test extends WP_UnitTestCase {
/**
* Test for `is_subscriptions_enabled`.
*
* @return void
*/
public function test_is_subscriptions_enabled() {
$this->assertTrue( WC_Stripe_Subscriptions_Helper::is_subscriptions_enabled() );
}

/**
* Test for `get_detached_subscriptions`.
*
* @return void
*/
public function test_get_detached_subscriptions() {
$subscription_id = 1;
$customer_id = 'cus_123';
$source_id = 'src_123';

$subscription = new WC_Subscription();
$subscription->set_id( $subscription_id );
$subscription->set_status( 'active' );
$subscription->save();

$subscription->update_meta_data( '_stripe_customer_id', $customer_id );
$subscription->update_meta_data( '_stripe_source_id', $source_id );
$subscription->save_meta_data();

WC_Subscriptions_Helpers::$wcs_get_subscriptions = [ $subscription ];

// Mock response from Stripe API.
$test_request = function () {
return [
'response' => 200,
'headers' => [ 'Content-Type' => 'application/json' ],
'body' => wp_json_encode(
[
'customer' => null,
]
),
];
};

add_filter( 'pre_http_request', $test_request, 10, 3 );

$expected = [
[
'id' => $subscription_id,
'customer_id' => $customer_id,
'change_payment_method_url' => $subscription->get_change_payment_method_url(),
],
];
$this->assertEquals( $expected, WC_Stripe_Subscriptions_Helper::get_detached_subscriptions() );

remove_filter( 'pre_http_request', $test_request, 10, 3 );

// Test cached version
$this->assertEquals( $expected, WC_Stripe_Subscriptions_Helper::get_detached_subscriptions() );
}
}
10 changes: 10 additions & 0 deletions tests/phpunit/helpers/class-wc-helper-subscription.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,14 @@ function( $order_types ) {
public function get_type() {
return $this->order_type;
}

/**
* Generates a URL to add or change the subscription's payment method from the my account page.
*
* @return string
*/
public function get_change_payment_method_url() {
$change_payment_method_url = wc_get_endpoint_url( 'subscription-payment-method', $this->get_id(), wc_get_page_permalink( 'myaccount' ) );
return apply_filters( 'wcs_get_change_payment_method_url', $change_payment_method_url, $this->get_id() );
}
}
3 changes: 2 additions & 1 deletion tests/phpunit/helpers/class-wc-subscriptions-helper.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@ function wcs_get_subscriptions_for_order( $order ) {
/**
* A function to mock wcs_get_subscriptions.
*
* @param array $args A set of name value pairs to determine the return value.
* @return array
*/
function wcs_get_subscriptions() {
function wcs_get_subscriptions( $args ) {
if ( ! WC_Subscriptions_Helpers::$wcs_get_subscriptions ) {
return [];
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2648,10 +2648,6 @@ public function test_set_payment_method_title_for_order() {

WC_Subscriptions_Helpers::$wcs_get_subscriptions_for_order = [ $mock_subscription_0, $mock_subscription_1 ];

$this->mock_gateway->expects( $this->exactly( 4 ) ) // 4 times because we test 4 payment methods.
->method( 'is_subscriptions_enabled' )
->willReturn( true );

/**
* SEPA
*/
Expand Down
1 change: 1 addition & 0 deletions woocommerce-gateway-stripe.php
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@ public function init() {
require_once __DIR__ . '/includes/class-wc-stripe-helper.php';
include_once __DIR__ . '/includes/class-wc-stripe-api.php';
include_once __DIR__ . '/includes/class-wc-stripe-mode.php';
require_once __DIR__ . '/includes/compat/class-wc-stripe-subscriptions-helper.php';
require_once __DIR__ . '/includes/compat/trait-wc-stripe-subscriptions-utilities.php';
require_once __DIR__ . '/includes/compat/trait-wc-stripe-subscriptions.php';
require_once __DIR__ . '/includes/compat/trait-wc-stripe-pre-orders.php';
Expand Down
Loading