From 2c37ea23c15693a1a0c94dc1aab808ba26060d37 Mon Sep 17 00:00:00 2001 From: Alfredo Sumaran Date: Fri, 19 Jul 2024 19:13:29 -0500 Subject: [PATCH] Allow the purchase of physical subscriptions using ECE if no shipping options are defined (#9134) --- ...duct-if-no-shipping-methods-are-configured | 4 + ...yments-express-checkout-button-handler.php | 15 ++ ...ayments-express-checkout-button-helper.php | 156 +++++++++++++++++- 3 files changed, 174 insertions(+), 1 deletion(-) create mode 100644 changelog/9106-ece-checkout-fails-for-physical-subscription-product-if-no-shipping-methods-are-configured diff --git a/changelog/9106-ece-checkout-fails-for-physical-subscription-product-if-no-shipping-methods-are-configured b/changelog/9106-ece-checkout-fails-for-physical-subscription-product-if-no-shipping-methods-are-configured new file mode 100644 index 00000000000..f292b91c62b --- /dev/null +++ b/changelog/9106-ece-checkout-fails-for-physical-subscription-product-if-no-shipping-methods-are-configured @@ -0,0 +1,4 @@ +Significance: minor +Type: fix + +Allow the purchase of physical subscriptions using ECE if no shipping options are defined. diff --git a/includes/express-checkout/class-wc-payments-express-checkout-button-handler.php b/includes/express-checkout/class-wc-payments-express-checkout-button-handler.php index 9c62be93c45..1b6f68f6275 100644 --- a/includes/express-checkout/class-wc-payments-express-checkout-button-handler.php +++ b/includes/express-checkout/class-wc-payments-express-checkout-button-handler.php @@ -92,6 +92,7 @@ public function init() { add_action( 'template_redirect', [ $this, 'handle_express_checkout_redirect' ] ); add_filter( 'woocommerce_login_redirect', [ $this, 'get_login_redirect_url' ], 10, 3 ); add_filter( 'woocommerce_registration_redirect', [ $this, 'get_login_redirect_url' ], 10, 3 ); + add_filter( 'woocommerce_cart_needs_shipping_address', [ $this, 'filter_cart_needs_shipping_address' ], 11, 1 ); add_action( 'wp_enqueue_scripts', [ $this, 'scripts' ] ); add_action( 'before_woocommerce_pay_form', [ $this, 'display_pay_for_order_page_html' ], 1 ); add_filter( 'woocommerce_gateway_title', [ $this, 'filter_gateway_title' ], 10, 2 ); @@ -405,6 +406,20 @@ public function get_login_redirect_url( $redirect ) { return $url; } + + /** + * Determine whether to filter the cart needs shipping address. + * + * @param boolean $needs_shipping_address Whether the cart needs a shipping address. + */ + public function filter_cart_needs_shipping_address( $needs_shipping_address ) { + if ( $this->has_subscription_product() && wc_get_shipping_method_count( true, true ) === 0 ) { + return false; + } + + return $needs_shipping_address; + } + /** * Filters the gateway title to reflect the button type used. * diff --git a/tests/unit/test-class-wc-payments-express-checkout-button-helper.php b/tests/unit/test-class-wc-payments-express-checkout-button-helper.php index 875e07f588b..18528d4a4bc 100644 --- a/tests/unit/test-class-wc-payments-express-checkout-button-helper.php +++ b/tests/unit/test-class-wc-payments-express-checkout-button-helper.php @@ -35,6 +35,54 @@ class WC_Payments_Express_Checkout_Button_Helper_Test extends WCPAY_UnitTestCase */ private $express_checkout_helper; + /** + * Test shipping zone. + * + * @var WC_Shipping_Zone + */ + private $zone; + + /** + * Flat rate shipping method instance id + * + * @var int + */ + private $flat_rate_id; + + /** + * Flat rate shipping method instance id + * + * @var int + */ + private $local_pickup_id; + + /** + * Express Checkout Helper instance. + * + * @var WC_Payments_Express_Checkout_Button_Helper + */ + private $mock_express_checkout_helper; + + /** + * Express Checkout Ajax Handler instance. + * + * @var WC_Payments_Express_Checkout_Ajax_Handler + */ + private $mock_express_checkout_ajax_handler; + + /** + * Express Checkout ECE Button Handler instance. + * + * @var WC_Payments_Express_Checkout_Button_Handler + */ + private $mock_express_checkout_ece_button_handler; + + /** + * Test product to add to the cart + * @var WC_Product_Simple + */ + private $simple_product; + /** * Sets up things all tests need. */ @@ -44,11 +92,52 @@ public function set_up() { $this->mock_wcpay_account = $this->createMock( WC_Payments_Account::class ); $this->mock_wcpay_gateway = $this->make_wcpay_gateway(); - $this->mock_express_checkout_helper = new WC_Payments_Express_Checkout_Button_Helper( $this->mock_wcpay_gateway, $this->mock_wcpay_account ); + $this->mock_express_checkout_helper = new WC_Payments_Express_Checkout_Button_Helper( $this->mock_wcpay_gateway, $this->mock_wcpay_account ); + $this->mock_express_checkout_ajax_handler = $this->getMockBuilder( WC_Payments_Express_Checkout_Ajax_Handler::class ) + ->setConstructorArgs( + [ + $this->mock_express_checkout_helper, + ] + ) + ->getMock(); + + $this->mock_ece_button_helper = $this->getMockBuilder( WC_Payments_Express_Checkout_Button_Helper::class ) + ->setConstructorArgs( + [ + $this->mock_wcpay_gateway, + $this->mock_wcpay_account, + ] + ) + ->getMock(); + + WC_Helper_Shipping::delete_simple_flat_rate(); + $zone = new WC_Shipping_Zone(); + $zone->set_zone_name( 'Worldwide' ); + $zone->set_zone_order( 1 ); + $zone->save(); + + $this->flat_rate_id = $zone->add_shipping_method( 'flat_rate' ); + self::set_shipping_method_cost( $this->flat_rate_id, '5' ); + + $this->local_pickup_id = $zone->add_shipping_method( 'local_pickup' ); + self::set_shipping_method_cost( $this->local_pickup_id, '1' ); + + $this->zone = $zone; + + $this->simple_product = WC_Helper_Product::create_simple_product(); + + WC()->session->init(); + WC()->cart->add_to_cart( $this->simple_product->get_id(), 1 ); + $this->mock_express_checkout_helper->update_shipping_method( [ self::get_shipping_option_rate_id( $this->flat_rate_id ) ] ); + WC()->cart->calculate_totals(); } public function tear_down() { parent::tear_down(); + WC_Subscriptions_Cart::set_cart_contains_subscription( false ); + WC()->cart->empty_cart(); + WC()->session->cleanup_sessions(); + $this->zone->delete(); remove_filter( 'wc_tax_enabled', '__return_true' ); remove_filter( 'wc_tax_enabled', '__return_false' ); remove_filter( 'pre_option_woocommerce_tax_display_cart', [ $this, '__return_excl' ] ); @@ -152,4 +241,69 @@ function () { $this->assertEquals( 'Google Pay (via WooPayments)', $result ); } + + public function test_filter_cart_needs_shipping_address_returns_false() { + sleep( 1 ); + $this->zone->delete_shipping_method( $this->flat_rate_id ); + $this->zone->delete_shipping_method( $this->local_pickup_id ); + + WC_Subscriptions_Cart::set_cart_contains_subscription( true ); + + $this->mock_ece_button_helper + ->method( 'is_product' ) + ->willReturn( true ); + + $this->mock_express_checkout_ece_button_handler = new WC_Payments_Express_Checkout_Button_Handler( + $this->mock_wcpay_account, + $this->mock_wcpay_gateway, + $this->mock_ece_button_helper, + $this->mock_express_checkout_ajax_handler + ); + + $this->assertFalse( $this->mock_express_checkout_ece_button_handler->filter_cart_needs_shipping_address( true ) ); + } + + public function test_filter_cart_needs_shipping_address_returns_true() { + WC_Subscriptions_Cart::set_cart_contains_subscription( true ); + + $this->mock_ece_button_helper + ->method( 'is_product' ) + ->willReturn( true ); + + $this->mock_express_checkout_ece_button_handler = new WC_Payments_Express_Checkout_Button_Handler( + $this->mock_wcpay_account, + $this->mock_wcpay_gateway, + $this->mock_ece_button_helper, + $this->mock_express_checkout_ajax_handler + ); + + $this->assertTrue( $this->mock_express_checkout_ece_button_handler->filter_cart_needs_shipping_address( true ) ); + } + + /** + * Sets shipping method cost + * + * @param string $instance_id Shipping method instance id + * @param string $cost Shipping method cost in USD + */ + private static function set_shipping_method_cost( $instance_id, $cost ) { + $method = WC_Shipping_Zones::get_shipping_method( $instance_id ); + $option_key = $method->get_instance_option_key(); + $options = get_option( $option_key ); + $options['cost'] = $cost; + update_option( $option_key, $options ); + } + + /** + * Retrieves rate id by shipping method instance id. + * + * @param string $instance_id Shipping method instance id. + * + * @return string Shipping option instance rate id. + */ + private static function get_shipping_option_rate_id( $instance_id ) { + $method = WC_Shipping_Zones::get_shipping_method( $instance_id ); + + return $method->get_rate_id(); + } }