diff --git a/modules/ppcp-api-client/src/Factory/ShippingPreferenceFactory.php b/modules/ppcp-api-client/src/Factory/ShippingPreferenceFactory.php index 3c3cee0df..7416560b3 100644 --- a/modules/ppcp-api-client/src/Factory/ShippingPreferenceFactory.php +++ b/modules/ppcp-api-client/src/Factory/ShippingPreferenceFactory.php @@ -10,6 +10,9 @@ declare(strict_types=1); namespace WooCommerce\PayPalCommerce\ApiClient\Factory; use WC_Cart; +use WC_Order; +use WC_Order_Item_Product; +use WC_Product; use WooCommerce\PayPalCommerce\ApiClient\Entity\ExperienceContext; use WooCommerce\PayPalCommerce\ApiClient\Entity\PurchaseUnit; @@ -31,7 +34,8 @@ class ShippingPreferenceFactory { PurchaseUnit $purchase_unit, string $context, ?WC_Cart $cart = null, - string $funding_source = '' + string $funding_source = '', + ?WC_Order $wc_order = null ): string { $contains_physical_goods = $purchase_unit->contains_physical_goods(); if ( ! $contains_physical_goods ) { @@ -39,8 +43,12 @@ class ShippingPreferenceFactory { } $has_shipping = null !== $purchase_unit->shipping(); - $needs_shipping = $cart && $cart->needs_shipping(); - $shipping_address_is_fixed = $needs_shipping && 'checkout' === $context; + $needs_shipping = ( $wc_order && $this->wc_order_needs_shipping( $wc_order ) ) || ( $cart && $cart->needs_shipping() ); + $shipping_address_is_fixed = $needs_shipping && in_array( $context, array( 'checkout', 'pay-now' ), true ); + + if ( ! $needs_shipping ) { + return ExperienceContext::SHIPPING_PREFERENCE_NO_SHIPPING; + } if ( $shipping_address_is_fixed ) { // Checkout + no address given? Probably something weird happened, like no form validation? @@ -61,4 +69,26 @@ class ShippingPreferenceFactory { return ExperienceContext::SHIPPING_PREFERENCE_GET_FROM_FILE; } + + protected function wc_order_needs_shipping( WC_Order $wc_order ): bool { + // WC 9.9.0+. + if ( method_exists( $wc_order, 'needs_shipping' ) ) { + return $wc_order->needs_shipping(); + } + + if ( ! wc_shipping_enabled() || wc_get_shipping_method_count( true ) === 0 ) { + return false; + } + + foreach ( $wc_order->get_items() as $item ) { + if ( $item instanceof WC_Order_Item_Product ) { + $product = $item->get_product(); + if ( $product instanceof WC_Product && $product->needs_shipping() ) { + return true; + } + } + } + + return false; + } } diff --git a/modules/ppcp-button/src/Endpoint/CreateOrderEndpoint.php b/modules/ppcp-button/src/Endpoint/CreateOrderEndpoint.php index cd8b6c594..ec4ce107d 100644 --- a/modules/ppcp-button/src/Endpoint/CreateOrderEndpoint.php +++ b/modules/ppcp-button/src/Endpoint/CreateOrderEndpoint.php @@ -440,7 +440,8 @@ class CreateOrderEndpoint implements EndpointInterface { $this->purchase_unit, $this->parsed_request_data['context'], WC()->cart, - $funding_source + $funding_source, + $wc_order ); $action = in_array( $this->parsed_request_data['context'], $this->pay_now_contexts, true ) ? diff --git a/tests/PHPUnit/ApiClient/Factory/ShippingPreferenceFactoryTest.php b/tests/PHPUnit/ApiClient/Factory/ShippingPreferenceFactoryTest.php index d16dc2dac..99e9f5988 100644 --- a/tests/PHPUnit/ApiClient/Factory/ShippingPreferenceFactoryTest.php +++ b/tests/PHPUnit/ApiClient/Factory/ShippingPreferenceFactoryTest.php @@ -5,10 +5,14 @@ namespace WooCommerce\PayPalCommerce\ApiClient\Factory; use Mockery; use WC_Cart; +use WC_Order; +use WC_Order_Item_Product; +use WC_Product; use WooCommerce\PayPalCommerce\ApiClient\Entity\ExperienceContext; use WooCommerce\PayPalCommerce\ApiClient\Entity\PurchaseUnit; use WooCommerce\PayPalCommerce\ApiClient\Entity\Shipping; use WooCommerce\PayPalCommerce\TestCase; +use function Brain\Monkey\Functions\when; class ShippingPreferenceFactoryTest extends TestCase { @@ -18,6 +22,9 @@ class ShippingPreferenceFactoryTest extends TestCase { parent::setUp(); + when('wc_shipping_enabled')->justReturn(true); + when('wc_get_shipping_method_count')->justReturn(2); + $this->testee = new ShippingPreferenceFactory(); } @@ -29,9 +36,10 @@ class ShippingPreferenceFactoryTest extends TestCase string $context, ?WC_Cart $cart, string $funding_source, + ?WC_Order $wc_order, string $expected_result ) { - $result = $this->testee->from_state($purchase_unit, $context, $cart, $funding_source); + $result = $this->testee->from_state($purchase_unit, $context, $cart, $funding_source, $wc_order); self::assertEquals($expected_result, $result); } @@ -43,6 +51,7 @@ class ShippingPreferenceFactoryTest extends TestCase 'checkout', $this->createCart(true), '', + null, ExperienceContext::SHIPPING_PREFERENCE_SET_PROVIDED_ADDRESS, ]; yield [ @@ -50,6 +59,7 @@ class ShippingPreferenceFactoryTest extends TestCase 'checkout', $this->createCart(false), '', + null, ExperienceContext::SHIPPING_PREFERENCE_NO_SHIPPING, ]; yield [ @@ -57,6 +67,7 @@ class ShippingPreferenceFactoryTest extends TestCase 'checkout', $this->createCart(true), '', + null, ExperienceContext::SHIPPING_PREFERENCE_NO_SHIPPING, ]; yield [ @@ -64,6 +75,7 @@ class ShippingPreferenceFactoryTest extends TestCase 'checkout', $this->createCart(true), 'card', + null, ExperienceContext::SHIPPING_PREFERENCE_SET_PROVIDED_ADDRESS, ]; yield [ @@ -71,34 +83,39 @@ class ShippingPreferenceFactoryTest extends TestCase 'product', null, '', - ExperienceContext::SHIPPING_PREFERENCE_GET_FROM_FILE, + null, + ExperienceContext::SHIPPING_PREFERENCE_NO_SHIPPING ]; yield [ $this->createPurchaseUnit(true, null), 'pay-now', null, 'venmo', - ExperienceContext::SHIPPING_PREFERENCE_GET_FROM_FILE, + $this->createWcOrder(false), + ExperienceContext::SHIPPING_PREFERENCE_NO_SHIPPING ]; yield [ $this->createPurchaseUnit(true, Mockery::mock(Shipping::class)), 'pay-now', null, 'venmo', - ExperienceContext::SHIPPING_PREFERENCE_GET_FROM_FILE, + $this->createWcOrder(true), + ExperienceContext::SHIPPING_PREFERENCE_SET_PROVIDED_ADDRESS ]; yield [ $this->createPurchaseUnit(true, Mockery::mock(Shipping::class)), 'pay-now', null, 'card', - ExperienceContext::SHIPPING_PREFERENCE_SET_PROVIDED_ADDRESS, + $this->createWcOrder(true), + ExperienceContext::SHIPPING_PREFERENCE_SET_PROVIDED_ADDRESS ]; yield [ $this->createPurchaseUnit(true, null), 'pay-now', null, 'card', + $this->createWcOrder(false), ExperienceContext::SHIPPING_PREFERENCE_NO_SHIPPING, ]; } @@ -115,4 +132,16 @@ class ShippingPreferenceFactoryTest extends TestCase $cart->shouldReceive('needs_shipping')->andReturn($needsShipping); return $cart; } + + private function createWcOrder(bool $needsShipping): WC_Order { + $product = Mockery::mock(WC_Product::class); + $product->shouldReceive('needs_shipping')->andReturn($needsShipping); + + $item = Mockery::mock(WC_Order_Item_Product::class); + $item->shouldReceive('get_product')->andReturn($product); + + $wcOrder = Mockery::mock(WC_Order::class); + $wcOrder->shouldReceive('get_items')->andReturn([$item]); + return $wcOrder; + } }