From f7323210f0e5e50685cc64207be39d72719b5eb7 Mon Sep 17 00:00:00 2001 From: Alex P Date: Wed, 1 Mar 2023 10:20:04 +0200 Subject: [PATCH 01/23] Show funding source as payment method --- modules/ppcp-wc-gateway/services.php | 17 ++++++- .../FundingSource/FundingSourceRenderer.php | 46 ++++++++++++++++--- .../ppcp-wc-gateway/src/WCGatewayModule.php | 2 +- .../WcGateway/Gateway/WcGatewayTest.php | 7 ++- 4 files changed, 61 insertions(+), 11 deletions(-) diff --git a/modules/ppcp-wc-gateway/services.php b/modules/ppcp-wc-gateway/services.php index 1f3393337..e1b50d883 100644 --- a/modules/ppcp-wc-gateway/services.php +++ b/modules/ppcp-wc-gateway/services.php @@ -644,7 +644,7 @@ return array( >', '' ), - 'options' => $container->get( 'wcgateway.all-funding-sources' ), + 'options' => $container->get( 'wcgateway.settings.funding-sources' ), 'screens' => array( State::STATE_START, State::STATE_ONBOARDED, @@ -881,6 +881,17 @@ return array( 'sofort' => _x( 'Sofort', 'Name of payment method', 'woocommerce-paypal-payments' ), 'venmo' => _x( 'Venmo', 'Name of payment method', 'woocommerce-paypal-payments' ), 'trustly' => _x( 'Trustly', 'Name of payment method', 'woocommerce-paypal-payments' ), + 'paylater' => _x( 'Pay Later', 'Name of payment method', 'woocommerce-paypal-payments' ), + ); + }, + 'wcgateway.settings.funding-sources' => static function( ContainerInterface $container ): array { + return array_diff_key( + $container->get( 'wcgateway.all-funding-sources' ), + array_flip( + array( + 'paylater', + ) + ) ); }, @@ -951,9 +962,11 @@ return array( 'wcgateway.funding-source.renderer' => function ( ContainerInterface $container ) : FundingSourceRenderer { return new FundingSourceRenderer( - $container->get( 'wcgateway.settings' ) + $container->get( 'wcgateway.settings' ), + $container->get( 'wcgateway.all-funding-sources' ) ); }, + 'wcgateway.checkout-helper' => static function ( ContainerInterface $container ): CheckoutHelper { return new CheckoutHelper(); }, diff --git a/modules/ppcp-wc-gateway/src/FundingSource/FundingSourceRenderer.php b/modules/ppcp-wc-gateway/src/FundingSource/FundingSourceRenderer.php index e91d87574..a465db721 100644 --- a/modules/ppcp-wc-gateway/src/FundingSource/FundingSourceRenderer.php +++ b/modules/ppcp-wc-gateway/src/FundingSource/FundingSourceRenderer.php @@ -22,13 +22,32 @@ class FundingSourceRenderer { */ protected $settings; + /** + * Map funding source ID -> human-readable name. + * + * @var array + */ + protected $funding_sources; + + /** + * The IDs of the sources belonging to PayPal that do not need to mention "via PayPal". + * + * @var string[] + */ + protected $own_funding_sources = array( 'venmo', 'paylater' ); + /** * FundingSourceRenderer constructor. * - * @param ContainerInterface $settings The settings. + * @param ContainerInterface $settings The settings. + * @param array $funding_sources Map funding source ID -> human-readable name. */ - public function __construct( ContainerInterface $settings ) { - $this->settings = $settings; + public function __construct( + ContainerInterface $settings, + array $funding_sources + ) { + $this->settings = $settings; + $this->funding_sources = $funding_sources; } /** @@ -37,9 +56,17 @@ class FundingSourceRenderer { * @param string $id The ID of the funding source, such as 'venmo'. */ public function render_name( string $id ): string { - if ( 'venmo' === $id ) { - return __( 'Venmo', 'woocommerce-paypal-payments' ); + if ( array_key_exists( $id, $this->funding_sources ) ) { + if ( in_array( $id, $this->own_funding_sources, true ) ) { + return $this->funding_sources[ $id ]; + } + return sprintf( + /* translators: %s - Sofort, BLIK, iDeal, Mercado Pago, etc. */ + __( '%s (via PayPal)', 'woocommerce-paypal-payments' ), + $this->funding_sources[ $id ] + ); } + return $this->settings->has( 'title' ) ? $this->settings->get( 'title' ) : __( 'PayPal', 'woocommerce-paypal-payments' ); @@ -51,9 +78,14 @@ class FundingSourceRenderer { * @param string $id The ID of the funding source, such as 'venmo'. */ public function render_description( string $id ): string { - if ( 'venmo' === $id ) { - return __( 'Pay via Venmo.', 'woocommerce-paypal-payments' ); + if ( array_key_exists( $id, $this->funding_sources ) ) { + return sprintf( + /* translators: %s - Sofort, BLIK, iDeal, Mercado Pago, etc. */ + __( 'Pay via %s.', 'woocommerce-paypal-payments' ), + $this->funding_sources[ $id ] + ); } + return $this->settings->has( 'description' ) ? $this->settings->get( 'description' ) : __( 'Pay via PayPal.', 'woocommerce-paypal-payments' ); diff --git a/modules/ppcp-wc-gateway/src/WCGatewayModule.php b/modules/ppcp-wc-gateway/src/WCGatewayModule.php index 289eb178c..a8ae5eb60 100644 --- a/modules/ppcp-wc-gateway/src/WCGatewayModule.php +++ b/modules/ppcp-wc-gateway/src/WCGatewayModule.php @@ -179,7 +179,7 @@ class WCGatewayModule implements ModuleInterface { $c->get( 'onboarding.environment' ), $settings_status->is_pay_later_button_enabled(), $settings->has( 'disable_funding' ) ? $settings->get( 'disable_funding' ) : array(), - $c->get( 'wcgateway.all-funding-sources' ) + $c->get( 'wcgateway.settings.funding-sources' ) ); $assets->register_assets(); } diff --git a/tests/PHPUnit/WcGateway/Gateway/WcGatewayTest.php b/tests/PHPUnit/WcGateway/Gateway/WcGatewayTest.php index b62f013d7..26107abd0 100644 --- a/tests/PHPUnit/WcGateway/Gateway/WcGatewayTest.php +++ b/tests/PHPUnit/WcGateway/Gateway/WcGatewayTest.php @@ -59,7 +59,10 @@ class WcGatewayTest extends TestCase $this->environment = Mockery::mock(Environment::class); $this->paymentTokenRepository = Mockery::mock(PaymentTokenRepository::class); $this->logger = Mockery::mock(LoggerInterface::class); - $this->funding_source_renderer = new FundingSourceRenderer($this->settings); + $this->funding_source_renderer = new FundingSourceRenderer( + $this->settings, + ['venmo' => 'Venmo', 'paylater' => 'Pay Later', 'blik' => 'BLIK'] + ); $this->apiShopCountry = 'DE'; $this->onboardingState->shouldReceive('current_state')->andReturn(State::STATE_ONBOARDED); @@ -271,6 +274,8 @@ class WcGatewayTest extends TestCase return [ [null, 'PayPal', 'Pay via PayPal.'], ['venmo', 'Venmo', 'Pay via Venmo.'], + ['paylater', 'Pay Later', 'Pay via Pay Later.'], + ['blik', 'BLIK (via PayPal)', 'Pay via BLIK.'], ['qwerty', 'PayPal', 'Pay via PayPal.'], ]; } From 2c1459b6b9e494f83f427f31712ee13d325f2ef8 Mon Sep 17 00:00:00 2001 From: Alex P Date: Wed, 1 Mar 2023 10:23:14 +0200 Subject: [PATCH 02/23] Fix phpcs --- modules/ppcp-compat/src/PPEC/SubscriptionsHandler.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/ppcp-compat/src/PPEC/SubscriptionsHandler.php b/modules/ppcp-compat/src/PPEC/SubscriptionsHandler.php index 523909d77..c61d036e3 100644 --- a/modules/ppcp-compat/src/PPEC/SubscriptionsHandler.php +++ b/modules/ppcp-compat/src/PPEC/SubscriptionsHandler.php @@ -184,14 +184,14 @@ class SubscriptionsHandler { } // Are we on the WC > Subscriptions screen? - // phpcs:ignore WordPress.Security.NonceVerification.Missing + // phpcs:ignore WordPress.Security.NonceVerification $post_type = wc_clean( wp_unslash( $_GET['post_type'] ?? $_POST['post_type'] ?? '' ) ); if ( $post_type === 'shop_subscription' ) { return true; } // Are we editing an order or subscription tied to PPEC? - // phpcs:ignore WordPress.Security.NonceVerification.Missing + // phpcs:ignore WordPress.Security.NonceVerification $order_id = wc_clean( wp_unslash( $_GET['post'] ?? $_POST['post_ID'] ?? '' ) ); if ( $order_id ) { $order = wc_get_order( $order_id ); From 2adc0d57e1b016038e3c6b2ec028805e4ee60935 Mon Sep 17 00:00:00 2001 From: Alex P Date: Wed, 1 Mar 2023 16:17:36 +0200 Subject: [PATCH 03/23] Do not remove mini-cart location for pay later --- modules/ppcp-wc-gateway/services.php | 16 +---- .../src/Helper/SettingsStatus.php | 5 +- tests/PHPUnit/Helper/SettingsStub.php | 41 +++++++++++++ .../WcGateway/Settings/LocationsTest.php | 60 +++++++++++++++++++ 4 files changed, 105 insertions(+), 17 deletions(-) create mode 100644 tests/PHPUnit/Helper/SettingsStub.php create mode 100644 tests/PHPUnit/WcGateway/Settings/LocationsTest.php diff --git a/modules/ppcp-wc-gateway/services.php b/modules/ppcp-wc-gateway/services.php index 1f3393337..56b5289d6 100644 --- a/modules/ppcp-wc-gateway/services.php +++ b/modules/ppcp-wc-gateway/services.php @@ -1338,24 +1338,10 @@ return array( assert( $settings instanceof Settings ); $button_locations = $container->get( 'wcgateway.button.locations' ); - unset( $button_locations['mini-cart'] ); $smart_button_selected_locations = $settings->has( 'smart_button_locations' ) ? $settings->get( 'smart_button_locations' ) : array(); - $pay_later_button_locations = array(); - if ( empty( $smart_button_selected_locations ) ) { - return $pay_later_button_locations; - } - - foreach ( $button_locations as $location_key => $location ) { - if ( ! in_array( $location_key, $smart_button_selected_locations, true ) ) { - continue; - } - - $pay_later_button_locations[ $location_key ] = $location; - } - - return $pay_later_button_locations; + return array_intersect_key( $button_locations, array_flip( $smart_button_selected_locations ) ); }, 'wcgateway.ppcp-gateways' => static function ( ContainerInterface $container ): array { return array( diff --git a/modules/ppcp-wc-gateway/src/Helper/SettingsStatus.php b/modules/ppcp-wc-gateway/src/Helper/SettingsStatus.php index 7655ef8ad..02f5e7082 100644 --- a/modules/ppcp-wc-gateway/src/Helper/SettingsStatus.php +++ b/modules/ppcp-wc-gateway/src/Helper/SettingsStatus.php @@ -9,7 +9,6 @@ declare(strict_types=1); namespace WooCommerce\PayPalCommerce\WcGateway\Helper; -use WooCommerce\PayPalCommerce\WcGateway\Exception\NotFoundException; use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings; /** @@ -74,7 +73,9 @@ class SettingsStatus { * @return bool true if is enabled, otherwise false. */ public function is_pay_later_button_enabled_for_location( string $location ): bool { - return $this->is_pay_later_button_enabled() && $this->is_enabled_for_location( 'pay_later_button_locations', $location ); + return $this->is_pay_later_button_enabled() && + ( $this->is_enabled_for_location( 'pay_later_button_locations', $location ) || + ( 'product' === $location && $this->is_enabled_for_location( 'pay_later_button_locations', 'mini-cart' ) ) ); } /** diff --git a/tests/PHPUnit/Helper/SettingsStub.php b/tests/PHPUnit/Helper/SettingsStub.php new file mode 100644 index 000000000..5d799d92c --- /dev/null +++ b/tests/PHPUnit/Helper/SettingsStub.php @@ -0,0 +1,41 @@ +data = $data; + } + + public function get($id) { + if ( ! $this->has( $id ) ) { + throw new NotFoundException(); + } + + return $this->data[$id]; + } + + public function has($id) { + return array_key_exists( $id, $this->data ); + } + + public function set($id, $value) { + $this->data[$id] = $value; + } + + public function persist() { + } +} diff --git a/tests/PHPUnit/WcGateway/Settings/LocationsTest.php b/tests/PHPUnit/WcGateway/Settings/LocationsTest.php new file mode 100644 index 000000000..45cb63ffa --- /dev/null +++ b/tests/PHPUnit/WcGateway/Settings/LocationsTest.php @@ -0,0 +1,60 @@ +settings = new SettingsStub([]); + + $this->appContainer = $this->bootstrapModule([ + 'wcgateway.settings' => function () { + return $this->settings; + }, + ]); + } + + /** + * @dataProvider payLaterButtonLocationsData + */ + public function testPayLaterButtonLocations(array $selectedLocations, array $expectedResult) { + $this->settings->set('smart_button_locations', $selectedLocations); + + $result = $this->appContainer->get('wcgateway.settings.pay-later.button-locations'); + + self::assertEquals($expectedResult, $result); + } + + public function payLaterButtonLocationsData() + { + yield [ + ['product', 'cart', 'checkout', 'mini-cart'], + [ + 'product' => 'Single Product', + 'cart' => 'Cart', + 'checkout' => 'Checkout', + 'mini-cart' => 'Mini Cart', + ], + ]; + yield [ + ['cart', 'checkout'], + [ + 'cart' => 'Cart', + 'checkout' => 'Checkout', + ], + ]; + yield [ + [], + [], + ]; + } +} From bff551c13b996bb8e1db8a18602f66555267bfda Mon Sep 17 00:00:00 2001 From: Alex P Date: Wed, 1 Mar 2023 16:20:08 +0200 Subject: [PATCH 04/23] Fix service overriding in ModularTestCase doing it via container does not work because of the bootstrap bug --- bootstrap.php | 5 ++++- tests/PHPUnit/ModularTestCase.php | 21 +++++++++++++++------ 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/bootstrap.php b/bootstrap.php index 27c8f36a3..7b8f7271d 100644 --- a/bootstrap.php +++ b/bootstrap.php @@ -16,10 +16,13 @@ use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface; return function ( string $root_dir, - ContainerInterface ...$additional_containers + array $additional_containers = array(), + array $additional_modules = array() ): ContainerInterface { $modules = ( require "$root_dir/modules.php" )( $root_dir ); + $modules = array_merge( $modules, $additional_modules ); + /** * Use this filter to add custom module or remove some of existing ones. * Modules able to access container, add services and modify existing ones. diff --git a/tests/PHPUnit/ModularTestCase.php b/tests/PHPUnit/ModularTestCase.php index 8652fecfa..092690124 100644 --- a/tests/PHPUnit/ModularTestCase.php +++ b/tests/PHPUnit/ModularTestCase.php @@ -3,10 +3,10 @@ declare(strict_types=1); namespace WooCommerce\PayPalCommerce; -use WooCommerce\PayPalCommerce\Vendor\Dhii\Container\CompositeCachingServiceProvider; -use WooCommerce\PayPalCommerce\Vendor\Dhii\Container\DelegatingContainer; use WooCommerce\PayPalCommerce\Vendor\Dhii\Container\ServiceProvider; use Mockery\Adapter\Phpunit\MockeryPHPUnitIntegration; +use WooCommerce\PayPalCommerce\Vendor\Dhii\Modular\Module\ModuleInterface; +use WooCommerce\PayPalCommerce\Vendor\Interop\Container\ServiceProviderInterface; use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface; use function Brain\Monkey\Functions\when; @@ -58,13 +58,22 @@ class ModularTestCase extends TestCase */ protected function bootstrapModule(array $overriddenServices = []): ContainerInterface { - $overridingContainer = new DelegatingContainer(new CompositeCachingServiceProvider([ - new ServiceProvider($overriddenServices, []), - ])); + $module = new class ($overriddenServices) implements ModuleInterface { + public function __construct(array $services) { + $this->services = $services; + } + + public function setup(): ServiceProviderInterface{ + return new ServiceProvider($this->services, []); + } + + public function run(ContainerInterface $c): void { + } + }; $rootDir = ROOT_DIR; $bootstrap = require ("$rootDir/bootstrap.php"); - $appContainer = $bootstrap($rootDir, $overridingContainer); + $appContainer = $bootstrap($rootDir, [], [$module]); return $appContainer; } From 33bfc324274deecfa642a5d6122026b10e8a930d Mon Sep 17 00:00:00 2001 From: Alex P Date: Wed, 1 Mar 2023 16:20:31 +0200 Subject: [PATCH 05/23] Fix test warnings --- tests/PHPUnit/Button/Endpoint/ValidateCheckoutEndpointTest.php | 3 +++ tests/e2e/PHPUnit/Validation/ValidationTest.php | 2 ++ 2 files changed, 5 insertions(+) diff --git a/tests/PHPUnit/Button/Endpoint/ValidateCheckoutEndpointTest.php b/tests/PHPUnit/Button/Endpoint/ValidateCheckoutEndpointTest.php index 39d38bfb0..8d8a7dabc 100644 --- a/tests/PHPUnit/Button/Endpoint/ValidateCheckoutEndpointTest.php +++ b/tests/PHPUnit/Button/Endpoint/ValidateCheckoutEndpointTest.php @@ -5,6 +5,7 @@ namespace WooCommerce\PayPalCommerce\Button\Endpoint; use Exception; use Mockery; +use Mockery\Adapter\Phpunit\MockeryPHPUnitIntegration; use Psr\Log\LoggerInterface; use WooCommerce\PayPalCommerce\Button\Exception\ValidationException; use WooCommerce\PayPalCommerce\Button\Validation\CheckoutFormValidator; @@ -13,6 +14,8 @@ use function Brain\Monkey\Functions\expect; class ValidateCheckoutEndpointTest extends TestCase { + use MockeryPHPUnitIntegration; + private $requestData; private $formValidator; private $logger; diff --git a/tests/e2e/PHPUnit/Validation/ValidationTest.php b/tests/e2e/PHPUnit/Validation/ValidationTest.php index cf6d87f36..4c012e5c1 100644 --- a/tests/e2e/PHPUnit/Validation/ValidationTest.php +++ b/tests/e2e/PHPUnit/Validation/ValidationTest.php @@ -43,6 +43,8 @@ class ValidationTest extends TestCase 'terms-field'=>'1', 'terms'=>'on', ]); + + self::assertTrue(true); // no assertions warnings } public function testInvalid() From c08c02847102f81158ce2ad6462c4dd72eb4df7f Mon Sep 17 00:00:00 2001 From: Alex P Date: Wed, 1 Mar 2023 16:21:46 +0200 Subject: [PATCH 06/23] Make e2e php tests more reliable locally they were depending on the current shop country and shipping settings --- tests/e2e/PHPUnit/Order/PurchaseUnitTest.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/e2e/PHPUnit/Order/PurchaseUnitTest.php b/tests/e2e/PHPUnit/Order/PurchaseUnitTest.php index 5871dc100..23edee911 100644 --- a/tests/e2e/PHPUnit/Order/PurchaseUnitTest.php +++ b/tests/e2e/PHPUnit/Order/PurchaseUnitTest.php @@ -45,6 +45,10 @@ class PurchaseUnitTest extends TestCase $this->puFactory = $this->container->get( 'api.factory.purchase-unit' ); assert($this->puFactory instanceof PurchaseUnitFactory); + + add_filter('woocommerce_get_base_location', function () { + return 'AQ'; + }); } public function tearDown(): void @@ -195,6 +199,7 @@ class PurchaseUnitTest extends TestCase $product->set_regular_price((string) $data['price']); $product->set_tax_status('taxable'); $product->set_tax_class(''); + $product->set_virtual(true); $product->save(); From 7e16b39bbb7cae51c79b4e3fdf3cb2f93ded4d81 Mon Sep 17 00:00:00 2001 From: Alex P Date: Fri, 3 Mar 2023 09:48:45 +0200 Subject: [PATCH 07/23] Remove shortcodes from description --- .../src/Factory/ItemFactory.php | 15 +++++++++-- .../ApiClient/Factory/ItemFactoryTest.php | 26 ++++++++----------- 2 files changed, 24 insertions(+), 17 deletions(-) diff --git a/modules/ppcp-api-client/src/Factory/ItemFactory.php b/modules/ppcp-api-client/src/Factory/ItemFactory.php index c71e7ddfa..0c5e9b542 100644 --- a/modules/ppcp-api-client/src/Factory/ItemFactory.php +++ b/modules/ppcp-api-client/src/Factory/ItemFactory.php @@ -58,7 +58,7 @@ class ItemFactory { mb_substr( $product->get_name(), 0, 127 ), new Money( $price, $this->currency ), $quantity, - substr( wp_strip_all_tags( $product->get_description() ), 0, 127 ) ?: '', + $this->prepare_description( $product->get_description() ), null, $product->get_sku(), ( $product->is_virtual() ) ? Item::DIGITAL_GOODS : Item::PHYSICAL_GOODS @@ -130,7 +130,7 @@ class ItemFactory { mb_substr( $item->get_name(), 0, 127 ), new Money( $price_without_tax_rounded, $currency ), $quantity, - substr( wp_strip_all_tags( $product instanceof WC_Product ? $product->get_description() : '' ), 0, 127 ) ?: '', + $product instanceof WC_Product ? $this->prepare_description( $product->get_description() ) : '', null, $product instanceof WC_Product ? $product->get_sku() : '', ( $product instanceof WC_Product && $product->is_virtual() ) ? Item::DIGITAL_GOODS : Item::PHYSICAL_GOODS @@ -198,4 +198,15 @@ class ItemFactory { $category ); } + + /** + * Cleanups the description and prepares it for sending to PayPal. + * + * @param string $description Item description. + * @return string + */ + protected function prepare_description( string $description ): string { + $description = strip_shortcodes( wp_strip_all_tags( $description ) ); + return substr( $description, 0, 127 ) ?: ''; + } } diff --git a/tests/PHPUnit/ApiClient/Factory/ItemFactoryTest.php b/tests/PHPUnit/ApiClient/Factory/ItemFactoryTest.php index 640dffcdf..3f72dab07 100644 --- a/tests/PHPUnit/ApiClient/Factory/ItemFactoryTest.php +++ b/tests/PHPUnit/ApiClient/Factory/ItemFactoryTest.php @@ -43,9 +43,8 @@ class ItemFactoryTest extends TestCase ->expects('get_cart_contents') ->andReturn($items); - expect('wp_strip_all_tags') - ->with('description') - ->andReturn('description'); + expect('wp_strip_all_tags')->andReturnFirstArg(); + expect('strip_shortcodes')->andReturnFirstArg(); $woocommerce = Mockery::mock(\WooCommerce::class); $session = Mockery::mock(\WC_Session::class); @@ -99,9 +98,8 @@ class ItemFactoryTest extends TestCase ->expects('get_cart_contents') ->andReturn($items); - expect('wp_strip_all_tags') - ->with('description') - ->andReturn('description'); + expect('wp_strip_all_tags')->andReturnFirstArg(); + expect('strip_shortcodes')->andReturnFirstArg(); $woocommerce = Mockery::mock(\WooCommerce::class); $session = Mockery::mock(\WC_Session::class); @@ -130,9 +128,9 @@ class ItemFactoryTest extends TestCase $product ->expects('is_virtual') ->andReturn(false); - expect('wp_strip_all_tags') - ->with('description') - ->andReturn('description'); + + expect('wp_strip_all_tags')->andReturnFirstArg(); + expect('strip_shortcodes')->andReturnFirstArg(); $item = Mockery::mock(\WC_Order_Item_Product::class); $item @@ -190,9 +188,8 @@ class ItemFactoryTest extends TestCase ->expects('is_virtual') ->andReturn(true); - expect('wp_strip_all_tags') - ->with('description') - ->andReturn('description'); + expect('wp_strip_all_tags')->andReturnFirstArg(); + expect('strip_shortcodes')->andReturnFirstArg(); $item = Mockery::mock(\WC_Order_Item_Product::class); $item @@ -245,9 +242,8 @@ class ItemFactoryTest extends TestCase ->expects('is_virtual') ->andReturn(true); - expect('wp_strip_all_tags') - ->with($description) - ->andReturn(mb_substr( $description, 0, 127 )); + expect('wp_strip_all_tags')->andReturnFirstArg(); + expect('strip_shortcodes')->andReturnFirstArg(); $item = Mockery::mock(\WC_Order_Item_Product::class); $item From 8bfc58f0da111d0e0046f817caf2608dee733e73 Mon Sep 17 00:00:00 2001 From: Alex P Date: Mon, 6 Mar 2023 17:07:31 +0200 Subject: [PATCH 08/23] Fix duplicated auth error --- .../src/Settings/SettingsListener.php | 26 ++++------------ .../ppcp-wc-gateway/src/WCGatewayModule.php | 31 ++++++++++++++----- 2 files changed, 30 insertions(+), 27 deletions(-) diff --git a/modules/ppcp-wc-gateway/src/Settings/SettingsListener.php b/modules/ppcp-wc-gateway/src/Settings/SettingsListener.php index 980b0db80..9235f827a 100644 --- a/modules/ppcp-wc-gateway/src/Settings/SettingsListener.php +++ b/modules/ppcp-wc-gateway/src/Settings/SettingsListener.php @@ -204,6 +204,8 @@ class SettingsListener { /** * Prevent enabling both Pay Later messaging and PayPal vaulting + * + * @throws RuntimeException When API request fails. */ public function listen_for_vaulting_enabled() { if ( ! $this->is_valid_site_request() || State::STATE_ONBOARDED !== $this->state->current_state() ) { @@ -221,16 +223,7 @@ class SettingsListener { $this->settings->set( 'vault_enabled', false ); $this->settings->persist(); - add_action( - 'admin_notices', - function () use ( $exception ) { - printf( - '

%1$s

%2$s

', - esc_html__( 'Authentication with PayPal failed: ', 'woocommerce-paypal-payments' ) . esc_attr( $exception->getMessage() ), - wp_kses_post( __( 'Please verify your API Credentials and try again to connect your PayPal business account. Visit the plugin documentation for more information about the setup.', 'woocommerce-paypal-payments' ) ) - ); - } - ); + throw $exception; } /** @@ -532,6 +525,8 @@ class SettingsListener { /** * Prevent enabling tracking if it is not enabled for merchant account. + * + * @throws RuntimeException When API request fails. */ public function listen_for_tracking_enabled(): void { if ( State::STATE_ONBOARDED !== $this->state->current_state() ) { @@ -549,16 +544,7 @@ class SettingsListener { $this->settings->set( 'tracking_enabled', false ); $this->settings->persist(); - add_action( - 'admin_notices', - function () use ( $exception ) { - printf( - '

%1$s

%2$s

', - esc_html__( 'Authentication with PayPal failed: ', 'woocommerce-paypal-payments' ) . esc_attr( $exception->getMessage() ), - wp_kses_post( __( 'Please verify your API Credentials and try again to connect your PayPal business account. Visit the plugin documentation for more information about the setup.', 'woocommerce-paypal-payments' ) ) - ); - } - ); + throw $exception; } } } diff --git a/modules/ppcp-wc-gateway/src/WCGatewayModule.php b/modules/ppcp-wc-gateway/src/WCGatewayModule.php index 289eb178c..f885d298c 100644 --- a/modules/ppcp-wc-gateway/src/WCGatewayModule.php +++ b/modules/ppcp-wc-gateway/src/WCGatewayModule.php @@ -11,6 +11,7 @@ namespace WooCommerce\PayPalCommerce\WcGateway; use Psr\Log\LoggerInterface; use Throwable; +use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException; use WooCommerce\PayPalCommerce\Vendor\Dhii\Container\ServiceProvider; use WooCommerce\PayPalCommerce\Vendor\Dhii\Modular\Module\ModuleInterface; use WC_Order; @@ -453,14 +454,30 @@ class WCGatewayModule implements ModuleInterface { 'admin_init', static function () use ( $container ) { $listener = $container->get( 'wcgateway.settings.listener' ); - /** - * The settings listener. - * - * @var SettingsListener $listener - */ + assert( $listener instanceof SettingsListener ); + $listener->listen_for_merchant_id(); - $listener->listen_for_vaulting_enabled(); - $listener->listen_for_tracking_enabled(); + + try { + $listener->listen_for_vaulting_enabled(); + $listener->listen_for_tracking_enabled(); + } catch ( RuntimeException $exception ) { + add_action( + 'admin_notices', + function () use ( $exception ) { + printf( + '

%1$s

%2$s

', + esc_html__( 'Authentication with PayPal failed: ', 'woocommerce-paypal-payments' ) . esc_attr( $exception->getMessage() ), + wp_kses_post( + __( + 'Please verify your API Credentials and try again to connect your PayPal business account. Visit the plugin documentation for more information about the setup.', + 'woocommerce-paypal-payments' + ) + ) + ); + } + ); + } } ); From c41cedc80db2fa0ab2ef2729c776ab0cab44af48 Mon Sep 17 00:00:00 2001 From: Alex P Date: Tue, 7 Mar 2023 09:46:45 +0200 Subject: [PATCH 09/23] Change "Enabled" to "Available" in status text Now it is more clear because "enabled" can be understood as enabled gateway, while it only means that it is enabled in the PayPal account. --- modules/ppcp-wc-gateway/services.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/ppcp-wc-gateway/services.php b/modules/ppcp-wc-gateway/services.php index 1f3393337..5fe7e29f0 100644 --- a/modules/ppcp-wc-gateway/services.php +++ b/modules/ppcp-wc-gateway/services.php @@ -1254,7 +1254,7 @@ return array( $dcc_enabled = $dcc_product_status->dcc_is_active(); - $enabled_status_text = esc_html__( 'Status: Enabled', 'woocommerce-paypal-payments' ); + $enabled_status_text = esc_html__( 'Status: Available', 'woocommerce-paypal-payments' ); $disabled_status_text = esc_html__( 'Status: Not yet enabled', 'woocommerce-paypal-payments' ); $dcc_button_text = $dcc_enabled @@ -1287,7 +1287,7 @@ return array( $pui_enabled = $pui_product_status->pui_is_active(); - $enabled_status_text = esc_html__( 'Status: Enabled', 'woocommerce-paypal-payments' ); + $enabled_status_text = esc_html__( 'Status: Available', 'woocommerce-paypal-payments' ); $disabled_status_text = esc_html__( 'Status: Not yet enabled', 'woocommerce-paypal-payments' ); $enable_pui_url = $environment->current_environment_is( Environment::PRODUCTION ) From 3d096fb2ad9846cc20944b226f8aab80c4998bc2 Mon Sep 17 00:00:00 2001 From: Alex P Date: Tue, 7 Mar 2023 16:20:01 +0200 Subject: [PATCH 10/23] Handle price suffix with price for product button check --- .../js/modules/ContextBootstrap/SingleProductBootstap.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/modules/ppcp-button/resources/js/modules/ContextBootstrap/SingleProductBootstap.js b/modules/ppcp-button/resources/js/modules/ContextBootstrap/SingleProductBootstap.js index 6c0a03283..a36c1d408 100644 --- a/modules/ppcp-button/resources/js/modules/ContextBootstrap/SingleProductBootstap.js +++ b/modules/ppcp-button/resources/js/modules/ContextBootstrap/SingleProductBootstap.js @@ -69,8 +69,13 @@ class SingleProductBootstap { () => { const priceEl = document.querySelector('.product .woocommerce-Price-amount'); // variable products show price like 10.00 - 20.00 here - if (priceEl && priceEl.parentElement.querySelectorAll('.woocommerce-Price-amount').length === 1) { - return priceEl.innerText; + // but the second price also can be the suffix with the price incl/excl tax + if (priceEl) { + const allPriceElements = Array.from(priceEl.parentElement.querySelectorAll('.woocommerce-Price-amount')) + .filter(el => !el.parentElement.classList.contains('woocommerce-price-suffix')); + if (allPriceElements.length === 1) { + return priceEl.innerText; + } } return null; }, From c77f20d2f95637d73b65b6c0fd98815377c0ea38 Mon Sep 17 00:00:00 2001 From: Alex P Date: Wed, 8 Mar 2023 17:36:38 +0200 Subject: [PATCH 11/23] Fix name to avoid confusion --- .../src/StatusReportModule.php | 4 +-- modules/ppcp-webhooks/services.php | 4 +-- .../src/IncomingWebhookEndpoint.php | 28 +++++++++---------- ...nfoStorage.php => WebhookEventStorage.php} | 4 +-- .../ppcp-webhooks/src/WebhookRegistrar.php | 24 ++++++++-------- 5 files changed, 32 insertions(+), 32 deletions(-) rename modules/ppcp-webhooks/src/{WebhookInfoStorage.php => WebhookEventStorage.php} (95%) diff --git a/modules/ppcp-status-report/src/StatusReportModule.php b/modules/ppcp-status-report/src/StatusReportModule.php index 3e380b069..abf84b83b 100644 --- a/modules/ppcp-status-report/src/StatusReportModule.php +++ b/modules/ppcp-status-report/src/StatusReportModule.php @@ -20,7 +20,7 @@ use WooCommerce\PayPalCommerce\ApiClient\Helper\DccApplies; use WooCommerce\PayPalCommerce\Button\Helper\MessagesApply; use WooCommerce\PayPalCommerce\Compat\PPEC\PPECHelper; use WooCommerce\PayPalCommerce\Onboarding\State; -use WooCommerce\PayPalCommerce\Webhooks\WebhookInfoStorage; +use WooCommerce\PayPalCommerce\Webhooks\WebhookEventStorage; /** * Class StatusReportModule @@ -62,7 +62,7 @@ class StatusReportModule implements ModuleInterface { $messages_apply = $c->get( 'button.helper.messages-apply' ); $last_webhook_storage = $c->get( 'webhook.last-webhook-storage' ); - assert( $last_webhook_storage instanceof WebhookInfoStorage ); + assert( $last_webhook_storage instanceof WebhookEventStorage ); $billing_agreements_endpoint = $c->get( 'api.endpoint.billing-agreements' ); assert( $billing_agreements_endpoint instanceof BillingAgreementsEndpoint ); diff --git a/modules/ppcp-webhooks/services.php b/modules/ppcp-webhooks/services.php index d7ed8013c..d58065a4a 100644 --- a/modules/ppcp-webhooks/services.php +++ b/modules/ppcp-webhooks/services.php @@ -198,8 +198,8 @@ return array( ); }, - 'webhook.last-webhook-storage' => static function ( ContainerInterface $container ): WebhookInfoStorage { - return new WebhookInfoStorage( $container->get( 'webhook.last-webhook-storage.key' ) ); + 'webhook.last-webhook-storage' => static function ( ContainerInterface $container ): WebhookEventStorage { + return new WebhookEventStorage( $container->get( 'webhook.last-webhook-storage.key' ) ); }, 'webhook.last-webhook-storage.key' => static function ( ContainerInterface $container ): string { return 'ppcp-last-webhook'; diff --git a/modules/ppcp-webhooks/src/IncomingWebhookEndpoint.php b/modules/ppcp-webhooks/src/IncomingWebhookEndpoint.php index 9f8bbcf7a..4a7631572 100644 --- a/modules/ppcp-webhooks/src/IncomingWebhookEndpoint.php +++ b/modules/ppcp-webhooks/src/IncomingWebhookEndpoint.php @@ -77,11 +77,11 @@ class IncomingWebhookEndpoint { private $simulation; /** - * The last webhook info storage. + * The last webhook event storage. * - * @var WebhookInfoStorage + * @var WebhookEventStorage */ - private $last_webhook_storage; + private $last_webhook_event_storage; /** * IncomingWebhookEndpoint constructor. @@ -92,7 +92,7 @@ class IncomingWebhookEndpoint { * @param bool $verify_request Whether requests need to be verified or not. * @param WebhookEventFactory $webhook_event_factory The webhook event factory. * @param WebhookSimulation $simulation The simulation handler. - * @param WebhookInfoStorage $last_webhook_storage The last webhook info storage. + * @param WebhookEventStorage $last_webhook_event_storage The last webhook event storage. * @param RequestHandler ...$handlers The handlers, which process a request in the end. */ public function __construct( @@ -102,18 +102,18 @@ class IncomingWebhookEndpoint { bool $verify_request, WebhookEventFactory $webhook_event_factory, WebhookSimulation $simulation, - WebhookInfoStorage $last_webhook_storage, + WebhookEventStorage $last_webhook_event_storage, RequestHandler ...$handlers ) { - $this->webhook_endpoint = $webhook_endpoint; - $this->webhook = $webhook; - $this->handlers = $handlers; - $this->logger = $logger; - $this->verify_request = $verify_request; - $this->webhook_event_factory = $webhook_event_factory; - $this->last_webhook_storage = $last_webhook_storage; - $this->simulation = $simulation; + $this->webhook_endpoint = $webhook_endpoint; + $this->webhook = $webhook; + $this->handlers = $handlers; + $this->logger = $logger; + $this->verify_request = $verify_request; + $this->webhook_event_factory = $webhook_event_factory; + $this->last_webhook_event_storage = $last_webhook_event_storage; + $this->simulation = $simulation; } /** @@ -186,7 +186,7 @@ class IncomingWebhookEndpoint { public function handle_request( \WP_REST_Request $request ): \WP_REST_Response { $event = $this->event_from_request( $request ); - $this->last_webhook_storage->save( $event ); + $this->last_webhook_event_storage->save( $event ); if ( $this->simulation->is_simulation_event( $event ) ) { $this->logger->info( 'Received simulated webhook.' ); diff --git a/modules/ppcp-webhooks/src/WebhookInfoStorage.php b/modules/ppcp-webhooks/src/WebhookEventStorage.php similarity index 95% rename from modules/ppcp-webhooks/src/WebhookInfoStorage.php rename to modules/ppcp-webhooks/src/WebhookEventStorage.php index 9fb9865f6..2c2370c0e 100644 --- a/modules/ppcp-webhooks/src/WebhookInfoStorage.php +++ b/modules/ppcp-webhooks/src/WebhookEventStorage.php @@ -12,9 +12,9 @@ namespace WooCommerce\PayPalCommerce\Webhooks; use WooCommerce\PayPalCommerce\ApiClient\Entity\WebhookEvent; /** - * Class WebhookInfoStorage + * Class WebhookEventStorage */ -class WebhookInfoStorage { +class WebhookEventStorage { /** * The WP option key. diff --git a/modules/ppcp-webhooks/src/WebhookRegistrar.php b/modules/ppcp-webhooks/src/WebhookRegistrar.php index c16aa9acd..03769aa61 100644 --- a/modules/ppcp-webhooks/src/WebhookRegistrar.php +++ b/modules/ppcp-webhooks/src/WebhookRegistrar.php @@ -45,11 +45,11 @@ class WebhookRegistrar { private $rest_endpoint; /** - * The last webhook info storage. + * The last webhook event storage. * - * @var WebhookInfoStorage + * @var WebhookEventStorage */ - private $last_webhook_storage; + private $last_webhook_event_storage; /** * The logger. @@ -64,22 +64,22 @@ class WebhookRegistrar { * @param WebhookFactory $webhook_factory The Webhook factory. * @param WebhookEndpoint $endpoint The Webhook endpoint. * @param IncomingWebhookEndpoint $rest_endpoint The WordPress Rest API endpoint. - * @param WebhookInfoStorage $last_webhook_storage The last webhook info storage. + * @param WebhookEventStorage $last_webhook_event_storage The last webhook event storage. * @param LoggerInterface $logger The logger. */ public function __construct( WebhookFactory $webhook_factory, WebhookEndpoint $endpoint, IncomingWebhookEndpoint $rest_endpoint, - WebhookInfoStorage $last_webhook_storage, + WebhookEventStorage $last_webhook_event_storage, LoggerInterface $logger ) { - $this->webhook_factory = $webhook_factory; - $this->endpoint = $endpoint; - $this->rest_endpoint = $rest_endpoint; - $this->last_webhook_storage = $last_webhook_storage; - $this->logger = $logger; + $this->webhook_factory = $webhook_factory; + $this->endpoint = $endpoint; + $this->rest_endpoint = $rest_endpoint; + $this->last_webhook_event_storage = $last_webhook_event_storage; + $this->logger = $logger; } /** @@ -102,7 +102,7 @@ class WebhookRegistrar { self::KEY, $created->to_array() ); - $this->last_webhook_storage->clear(); + $this->last_webhook_event_storage->clear(); $this->logger->info( 'Webhooks subscribed.' ); return true; } catch ( RuntimeException $error ) { @@ -131,7 +131,7 @@ class WebhookRegistrar { if ( $success ) { delete_option( self::KEY ); - $this->last_webhook_storage->clear(); + $this->last_webhook_event_storage->clear(); $this->logger->info( 'Webhooks deleted.' ); } return $success; From 3d3fe3718587834de36c3f2e7a1fdf6a059ca4c2 Mon Sep 17 00:00:00 2001 From: Alex P Date: Thu, 9 Mar 2023 10:46:25 +0200 Subject: [PATCH 12/23] Improve webhook deletion error output --- .../src/Endpoint/WebhookEndpoint.php | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/modules/ppcp-api-client/src/Endpoint/WebhookEndpoint.php b/modules/ppcp-api-client/src/Endpoint/WebhookEndpoint.php index 252c1a294..d8cc06dca 100644 --- a/modules/ppcp-api-client/src/Endpoint/WebhookEndpoint.php +++ b/modules/ppcp-api-client/src/Endpoint/WebhookEndpoint.php @@ -175,12 +175,12 @@ class WebhookEndpoint { * * @param Webhook $hook The webhook to delete. * - * @return bool * @throws RuntimeException If the request fails. + * @throws PayPalApiException If the request fails. */ - public function delete( Webhook $hook ): bool { + public function delete( Webhook $hook ): void { if ( ! $hook->id() ) { - return false; + return; } $bearer = $this->bearer->bearer(); @@ -198,7 +198,18 @@ class WebhookEndpoint { __( 'Not able to delete the webhook.', 'woocommerce-paypal-payments' ) ); } - return wp_remote_retrieve_response_code( $response ) === 204; + + $status_code = (int) wp_remote_retrieve_response_code( $response ); + if ( 204 !== $status_code ) { + $json = null; + if ( is_array( $response ) ) { + $json = json_decode( $response['body'] ); + } + throw new PayPalApiException( + $json, + $status_code + ); + } } /** From 4c593372471884267176c245d6f0c0a349afab3d Mon Sep 17 00:00:00 2001 From: Alex P Date: Thu, 9 Mar 2023 10:47:29 +0200 Subject: [PATCH 13/23] Delete all webhooks before adding --- .../src/OnboardingRESTController.php | 3 +- .../src/Endpoint/ResubscribeEndpoint.php | 2 -- modules/ppcp-webhooks/src/WebhookModule.php | 1 - .../ppcp-webhooks/src/WebhookRegistrar.php | 30 +++++++++---------- 4 files changed, 16 insertions(+), 20 deletions(-) diff --git a/modules/ppcp-onboarding/src/OnboardingRESTController.php b/modules/ppcp-onboarding/src/OnboardingRESTController.php index 4eac5ba72..283cf64be 100644 --- a/modules/ppcp-onboarding/src/OnboardingRESTController.php +++ b/modules/ppcp-onboarding/src/OnboardingRESTController.php @@ -12,6 +12,7 @@ namespace WooCommerce\PayPalCommerce\Onboarding; use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface; use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway; use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway; +use WooCommerce\PayPalCommerce\Webhooks\WebhookRegistrar; /** * Exposes and handles REST routes related to onboarding. @@ -249,7 +250,7 @@ class OnboardingRESTController { } $webhook_registrar = $this->container->get( 'webhook.registrar' ); - $webhook_registrar->unregister(); + assert( $webhook_registrar instanceof WebhookRegistrar ); $webhook_registrar->register(); return array(); diff --git a/modules/ppcp-webhooks/src/Endpoint/ResubscribeEndpoint.php b/modules/ppcp-webhooks/src/Endpoint/ResubscribeEndpoint.php index b504cc38a..9fe20a1ed 100644 --- a/modules/ppcp-webhooks/src/Endpoint/ResubscribeEndpoint.php +++ b/modules/ppcp-webhooks/src/Endpoint/ResubscribeEndpoint.php @@ -62,8 +62,6 @@ class ResubscribeEndpoint { // Validate nonce. $this->request_data->read_request( $this->nonce() ); - $this->registrar->unregister(); - if ( ! $this->registrar->register() ) { wp_send_json_error( 'Webhook subscription failed.', 500 ); return false; diff --git a/modules/ppcp-webhooks/src/WebhookModule.php b/modules/ppcp-webhooks/src/WebhookModule.php index 6f8669a8b..e9cc66909 100644 --- a/modules/ppcp-webhooks/src/WebhookModule.php +++ b/modules/ppcp-webhooks/src/WebhookModule.php @@ -152,7 +152,6 @@ class WebhookModule implements ModuleInterface { add_action( 'init', function () use ( $registrar ) { - $registrar->unregister(); $registrar->register(); } ); diff --git a/modules/ppcp-webhooks/src/WebhookRegistrar.php b/modules/ppcp-webhooks/src/WebhookRegistrar.php index 03769aa61..8f8919ec2 100644 --- a/modules/ppcp-webhooks/src/WebhookRegistrar.php +++ b/modules/ppcp-webhooks/src/WebhookRegistrar.php @@ -88,6 +88,8 @@ class WebhookRegistrar { * @return bool */ public function register(): bool { + $this->unregister(); + $webhook = $this->webhook_factory->for_url_and_events( $this->rest_endpoint->url(), $this->rest_endpoint->handled_event_types() @@ -113,27 +115,23 @@ class WebhookRegistrar { /** * Unregister webhooks with PayPal. - * - * @return bool */ - public function unregister(): bool { - $data = (array) get_option( self::KEY, array() ); - if ( ! $data ) { - return false; - } + public function unregister(): void { try { - $webhook = $this->webhook_factory->from_array( $data ); - $success = $this->endpoint->delete( $webhook ); + $webhooks = $this->endpoint->list(); + foreach ( $webhooks as $webhook ) { + try { + $this->endpoint->delete( $webhook ); + } catch ( RuntimeException $deletion_error ) { + $this->logger->error( "Failed to delete webhook {$webhook->id()}: {$deletion_error->getMessage()}" ); + } + } } catch ( RuntimeException $error ) { $this->logger->error( 'Failed to delete webhooks: ' . $error->getMessage() ); - return false; } - if ( $success ) { - delete_option( self::KEY ); - $this->last_webhook_event_storage->clear(); - $this->logger->info( 'Webhooks deleted.' ); - } - return $success; + delete_option( self::KEY ); + $this->last_webhook_event_storage->clear(); + $this->logger->info( 'Webhooks deleted.' ); } } From 8188faeec4c1e96d686500fdfe2aa3814509f276 Mon Sep 17 00:00:00 2001 From: Alex P Date: Fri, 10 Mar 2023 10:34:28 +0200 Subject: [PATCH 14/23] Redirect on any credentials change (including sandbox checkbox) otherwise we may use wrong state/host e.g. for webhook subscription --- .../src/Settings/SettingsListener.php | 12 +----------- .../WcGateway/Settings/SettingsListenerTest.php | 2 ++ 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/modules/ppcp-wc-gateway/src/Settings/SettingsListener.php b/modules/ppcp-wc-gateway/src/Settings/SettingsListener.php index 980b0db80..829e005b3 100644 --- a/modules/ppcp-wc-gateway/src/Settings/SettingsListener.php +++ b/modules/ppcp-wc-gateway/src/Settings/SettingsListener.php @@ -322,16 +322,6 @@ class SettingsListener { } $this->settings->persist(); - if ( $credentials_change_status ) { - if ( in_array( - $credentials_change_status, - array( self::CREDENTIALS_ADDED, self::CREDENTIALS_CHANGED ), - true - ) ) { - $this->webhook_registrar->register(); - } - } - if ( $this->cache->has( PayPalBearer::CACHE_KEY ) ) { $this->cache->delete( PayPalBearer::CACHE_KEY ); } @@ -345,7 +335,7 @@ class SettingsListener { } $redirect_url = false; - if ( self::CREDENTIALS_ADDED === $credentials_change_status ) { + if ( self::CREDENTIALS_UNCHANGED !== $credentials_change_status ) { $redirect_url = $this->get_onboarding_redirect_url(); } diff --git a/tests/PHPUnit/WcGateway/Settings/SettingsListenerTest.php b/tests/PHPUnit/WcGateway/Settings/SettingsListenerTest.php index bad2dda33..403217a1b 100644 --- a/tests/PHPUnit/WcGateway/Settings/SettingsListenerTest.php +++ b/tests/PHPUnit/WcGateway/Settings/SettingsListenerTest.php @@ -85,6 +85,8 @@ class SettingsListenerTest extends ModularTestCase $dcc_status_cache->shouldReceive('has') ->andReturn(false); + expect('wp_safe_redirect')->once(); + $testee->listen(); } } From 9adcfe4b7d76d578fcd4b565f15176b9d5c5f2a3 Mon Sep 17 00:00:00 2001 From: Alex P Date: Fri, 10 Mar 2023 10:39:13 +0200 Subject: [PATCH 15/23] Hide webhook elements when sandbox state is not matching the server --- .../ppcp-webhooks/resources/js/status-page.js | 22 +++++++++++++++++++ modules/ppcp-webhooks/services.php | 3 ++- .../Assets/WebhooksStatusPageAssets.php | 22 ++++++++++++++----- 3 files changed, 41 insertions(+), 6 deletions(-) diff --git a/modules/ppcp-webhooks/resources/js/status-page.js b/modules/ppcp-webhooks/resources/js/status-page.js index f477ec403..8f5334f51 100644 --- a/modules/ppcp-webhooks/resources/js/status-page.js +++ b/modules/ppcp-webhooks/resources/js/status-page.js @@ -1,3 +1,5 @@ +import {setVisibleByClass} from "../../../ppcp-button/resources/js/modules/Helper/Hiding" + document.addEventListener( 'DOMContentLoaded', () => { @@ -147,5 +149,25 @@ document.addEventListener( simulateBtn.prop('disabled', false); } }); + + const sandboxCheckbox = document.querySelector('#ppcp-sandbox_on'); + if (sandboxCheckbox) { + const setWebhooksVisibility = (show) => { + [ + '#field-webhook_status_heading', + '#field-webhooks_list', + '#field-webhooks_resubscribe', + '#field-webhooks_simulate', + ].forEach(selector => { + setVisibleByClass(selector, show, 'hide'); + }); + }; + + const serverSandboxState = PayPalCommerceGatewayWebhooksStatus.environment === 'sandbox'; + setWebhooksVisibility(serverSandboxState === sandboxCheckbox.checked); + sandboxCheckbox.addEventListener('click', () => { + setWebhooksVisibility(serverSandboxState === sandboxCheckbox.checked); + }); + } } ); diff --git a/modules/ppcp-webhooks/services.php b/modules/ppcp-webhooks/services.php index d58065a4a..9e330e812 100644 --- a/modules/ppcp-webhooks/services.php +++ b/modules/ppcp-webhooks/services.php @@ -167,7 +167,8 @@ return array( 'webhook.status.assets' => function( ContainerInterface $container ) : WebhooksStatusPageAssets { return new WebhooksStatusPageAssets( $container->get( 'webhook.module-url' ), - $container->get( 'ppcp.asset-version' ) + $container->get( 'ppcp.asset-version' ), + $container->get( 'onboarding.environment' ) ); }, diff --git a/modules/ppcp-webhooks/src/Status/Assets/WebhooksStatusPageAssets.php b/modules/ppcp-webhooks/src/Status/Assets/WebhooksStatusPageAssets.php index 28a9ddd84..475c2a85e 100644 --- a/modules/ppcp-webhooks/src/Status/Assets/WebhooksStatusPageAssets.php +++ b/modules/ppcp-webhooks/src/Status/Assets/WebhooksStatusPageAssets.php @@ -9,6 +9,7 @@ declare(strict_types=1); namespace WooCommerce\PayPalCommerce\Webhooks\Status\Assets; +use WooCommerce\PayPalCommerce\Onboarding\Environment; use WooCommerce\PayPalCommerce\Webhooks\Endpoint\ResubscribeEndpoint; use WooCommerce\PayPalCommerce\Webhooks\Endpoint\SimulateEndpoint; use WooCommerce\PayPalCommerce\Webhooks\Endpoint\SimulationStateEndpoint; @@ -33,18 +34,28 @@ class WebhooksStatusPageAssets { */ private $version; + /** + * The environment object. + * + * @var Environment + */ + private $environment; + /** * WebhooksStatusPageAssets constructor. * - * @param string $module_url The URL to the module. - * @param string $version The assets version. + * @param string $module_url The URL to the module. + * @param string $version The assets version. + * @param Environment $environment The environment object. */ public function __construct( string $module_url, - string $version + string $version, + Environment $environment ) { - $this->module_url = untrailingslashit( $module_url ); - $this->version = $version; + $this->module_url = untrailingslashit( $module_url ); + $this->version = $version; + $this->environment = $environment; } /** @@ -103,6 +114,7 @@ class WebhooksStatusPageAssets { 'tooLongDelayMessage' => __( 'Looks like the webhook cannot be received. Check that your website is accessible from the internet.', 'woocommerce-paypal-payments' ), ), ), + 'environment' => $this->environment->current_environment(), ); } From 6ac3b67d8760e5cb15c64318f17ae4f4283c1922 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Mar 2023 08:01:45 +0000 Subject: [PATCH 16/23] Bump loader-utils from 2.0.2 to 2.0.4 in /modules/ppcp-uninstall Bumps [loader-utils](https://github.com/webpack/loader-utils) from 2.0.2 to 2.0.4. - [Release notes](https://github.com/webpack/loader-utils/releases) - [Changelog](https://github.com/webpack/loader-utils/blob/v2.0.4/CHANGELOG.md) - [Commits](https://github.com/webpack/loader-utils/compare/v2.0.2...v2.0.4) --- updated-dependencies: - dependency-name: loader-utils dependency-type: indirect ... Signed-off-by: dependabot[bot] --- modules/ppcp-uninstall/yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/ppcp-uninstall/yarn.lock b/modules/ppcp-uninstall/yarn.lock index 54ef909ff..1bb98cbe5 100644 --- a/modules/ppcp-uninstall/yarn.lock +++ b/modules/ppcp-uninstall/yarn.lock @@ -1805,9 +1805,9 @@ loader-runner@^4.2.0: integrity sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg== loader-utils@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.2.tgz#d6e3b4fb81870721ae4e0868ab11dd638368c129" - integrity sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A== + version "2.0.4" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.4.tgz#8b5cb38b5c34a9a018ee1fc0e6a066d1dfcc528c" + integrity sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw== dependencies: big.js "^5.2.2" emojis-list "^3.0.0" From c9b088db4cd6a4c858498022d9ce117665bdf943 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Mar 2023 08:17:06 +0000 Subject: [PATCH 17/23] Bump loader-utils from 2.0.2 to 2.0.4 in /modules/ppcp-compat Bumps [loader-utils](https://github.com/webpack/loader-utils) from 2.0.2 to 2.0.4. - [Release notes](https://github.com/webpack/loader-utils/releases) - [Changelog](https://github.com/webpack/loader-utils/blob/v2.0.4/CHANGELOG.md) - [Commits](https://github.com/webpack/loader-utils/compare/v2.0.2...v2.0.4) --- updated-dependencies: - dependency-name: loader-utils dependency-type: indirect ... Signed-off-by: dependabot[bot] --- modules/ppcp-compat/yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/ppcp-compat/yarn.lock b/modules/ppcp-compat/yarn.lock index c790c4b52..1377741f8 100644 --- a/modules/ppcp-compat/yarn.lock +++ b/modules/ppcp-compat/yarn.lock @@ -1702,9 +1702,9 @@ loader-runner@^4.2.0: integrity sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg== loader-utils@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.2.tgz#d6e3b4fb81870721ae4e0868ab11dd638368c129" - integrity sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A== + version "2.0.4" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.4.tgz#8b5cb38b5c34a9a018ee1fc0e6a066d1dfcc528c" + integrity sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw== dependencies: big.js "^5.2.2" emojis-list "^3.0.0" From dd0787dfcc452a98f22f2029139cccce3df86675 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Mar 2023 08:39:09 +0000 Subject: [PATCH 18/23] Bump loader-utils from 2.0.2 to 2.0.4 in /modules/ppcp-onboarding Bumps [loader-utils](https://github.com/webpack/loader-utils) from 2.0.2 to 2.0.4. - [Release notes](https://github.com/webpack/loader-utils/releases) - [Changelog](https://github.com/webpack/loader-utils/blob/v2.0.4/CHANGELOG.md) - [Commits](https://github.com/webpack/loader-utils/compare/v2.0.2...v2.0.4) --- updated-dependencies: - dependency-name: loader-utils dependency-type: indirect ... Signed-off-by: dependabot[bot] --- modules/ppcp-onboarding/yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/ppcp-onboarding/yarn.lock b/modules/ppcp-onboarding/yarn.lock index 84a6766b7..74b5c80ed 100644 --- a/modules/ppcp-onboarding/yarn.lock +++ b/modules/ppcp-onboarding/yarn.lock @@ -1702,9 +1702,9 @@ loader-runner@^4.2.0: integrity sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg== loader-utils@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.2.tgz#d6e3b4fb81870721ae4e0868ab11dd638368c129" - integrity sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A== + version "2.0.4" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.4.tgz#8b5cb38b5c34a9a018ee1fc0e6a066d1dfcc528c" + integrity sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw== dependencies: big.js "^5.2.2" emojis-list "^3.0.0" From aaecb7a12fcab796b1a86b9b425fe1a281870968 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Mar 2023 10:35:25 +0000 Subject: [PATCH 19/23] Bump loader-utils from 2.0.2 to 2.0.4 in /modules/ppcp-button Bumps [loader-utils](https://github.com/webpack/loader-utils) from 2.0.2 to 2.0.4. - [Release notes](https://github.com/webpack/loader-utils/releases) - [Changelog](https://github.com/webpack/loader-utils/blob/v2.0.4/CHANGELOG.md) - [Commits](https://github.com/webpack/loader-utils/compare/v2.0.2...v2.0.4) --- updated-dependencies: - dependency-name: loader-utils dependency-type: indirect ... Signed-off-by: dependabot[bot] --- modules/ppcp-button/yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/ppcp-button/yarn.lock b/modules/ppcp-button/yarn.lock index 5d7c6fab8..2807fdfb1 100644 --- a/modules/ppcp-button/yarn.lock +++ b/modules/ppcp-button/yarn.lock @@ -1723,9 +1723,9 @@ loader-runner@^4.2.0: integrity sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg== loader-utils@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.2.tgz#d6e3b4fb81870721ae4e0868ab11dd638368c129" - integrity sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A== + version "2.0.4" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.4.tgz#8b5cb38b5c34a9a018ee1fc0e6a066d1dfcc528c" + integrity sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw== dependencies: big.js "^5.2.2" emojis-list "^3.0.0" From 9d96f7133c558dbb2ea0e7e239ba6b1827aff186 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Mar 2023 11:07:50 +0000 Subject: [PATCH 20/23] Bump loader-utils from 2.0.2 to 2.0.4 in /modules/ppcp-order-tracking Bumps [loader-utils](https://github.com/webpack/loader-utils) from 2.0.2 to 2.0.4. - [Release notes](https://github.com/webpack/loader-utils/releases) - [Changelog](https://github.com/webpack/loader-utils/blob/v2.0.4/CHANGELOG.md) - [Commits](https://github.com/webpack/loader-utils/compare/v2.0.2...v2.0.4) --- updated-dependencies: - dependency-name: loader-utils dependency-type: indirect ... Signed-off-by: dependabot[bot] --- modules/ppcp-order-tracking/yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/ppcp-order-tracking/yarn.lock b/modules/ppcp-order-tracking/yarn.lock index 54ef909ff..1bb98cbe5 100644 --- a/modules/ppcp-order-tracking/yarn.lock +++ b/modules/ppcp-order-tracking/yarn.lock @@ -1805,9 +1805,9 @@ loader-runner@^4.2.0: integrity sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg== loader-utils@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.2.tgz#d6e3b4fb81870721ae4e0868ab11dd638368c129" - integrity sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A== + version "2.0.4" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.4.tgz#8b5cb38b5c34a9a018ee1fc0e6a066d1dfcc528c" + integrity sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw== dependencies: big.js "^5.2.2" emojis-list "^3.0.0" From 8f3c548c76647a0974a11c667df59f22a273d653 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Mar 2023 11:20:41 +0000 Subject: [PATCH 21/23] Bump loader-utils from 2.0.0 to 2.0.4 in /modules/ppcp-webhooks Bumps [loader-utils](https://github.com/webpack/loader-utils) from 2.0.0 to 2.0.4. - [Release notes](https://github.com/webpack/loader-utils/releases) - [Changelog](https://github.com/webpack/loader-utils/blob/v2.0.4/CHANGELOG.md) - [Commits](https://github.com/webpack/loader-utils/compare/v2.0.0...v2.0.4) --- updated-dependencies: - dependency-name: loader-utils dependency-type: indirect ... Signed-off-by: dependabot[bot] --- modules/ppcp-webhooks/yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/ppcp-webhooks/yarn.lock b/modules/ppcp-webhooks/yarn.lock index c25ba6dd3..99bd79caa 100644 --- a/modules/ppcp-webhooks/yarn.lock +++ b/modules/ppcp-webhooks/yarn.lock @@ -1776,9 +1776,9 @@ loader-runner@^4.2.0: integrity sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw== loader-utils@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.0.tgz#e4cace5b816d425a166b5f097e10cd12b36064b0" - integrity sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ== + version "2.0.4" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.4.tgz#8b5cb38b5c34a9a018ee1fc0e6a066d1dfcc528c" + integrity sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw== dependencies: big.js "^5.2.2" emojis-list "^3.0.0" From c11d738f7b51ed68dc48a071941154eb3ca138dd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Mar 2023 14:43:50 +0000 Subject: [PATCH 22/23] Bump loader-utils from 2.0.0 to 2.0.4 in /modules/ppcp-vaulting Bumps [loader-utils](https://github.com/webpack/loader-utils) from 2.0.0 to 2.0.4. - [Release notes](https://github.com/webpack/loader-utils/releases) - [Changelog](https://github.com/webpack/loader-utils/blob/v2.0.4/CHANGELOG.md) - [Commits](https://github.com/webpack/loader-utils/compare/v2.0.0...v2.0.4) --- updated-dependencies: - dependency-name: loader-utils dependency-type: indirect ... Signed-off-by: dependabot[bot] --- modules/ppcp-vaulting/yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/ppcp-vaulting/yarn.lock b/modules/ppcp-vaulting/yarn.lock index c25ba6dd3..99bd79caa 100644 --- a/modules/ppcp-vaulting/yarn.lock +++ b/modules/ppcp-vaulting/yarn.lock @@ -1776,9 +1776,9 @@ loader-runner@^4.2.0: integrity sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw== loader-utils@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.0.tgz#e4cace5b816d425a166b5f097e10cd12b36064b0" - integrity sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ== + version "2.0.4" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.4.tgz#8b5cb38b5c34a9a018ee1fc0e6a066d1dfcc528c" + integrity sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw== dependencies: big.js "^5.2.2" emojis-list "^3.0.0" From cc21dd78e7ecb408560fee56814b2cae56160d5a Mon Sep 17 00:00:00 2001 From: Alex P Date: Thu, 16 Mar 2023 18:13:41 +0200 Subject: [PATCH 23/23] Fix tests, add http redirector --- modules/ppcp-wc-gateway/services.php | 3 +- .../src/Settings/SettingsListener.php | 41 +++++++++++-------- src/Http/RedirectorInterface.php | 22 ++++++++++ src/Http/WpRedirector.php | 25 +++++++++++ src/services.php | 6 +++ tests/PHPUnit/Helper/RedirectorStub.php | 14 +++++++ .../Helper/StubRedirectionException.php | 10 +++++ tests/PHPUnit/ModularTestCase.php | 7 ++++ .../Settings/SettingsListenerTest.php | 7 +++- 9 files changed, 116 insertions(+), 19 deletions(-) create mode 100644 src/Http/RedirectorInterface.php create mode 100644 src/Http/WpRedirector.php create mode 100644 tests/PHPUnit/Helper/RedirectorStub.php create mode 100644 tests/PHPUnit/Helper/StubRedirectionException.php diff --git a/modules/ppcp-wc-gateway/services.php b/modules/ppcp-wc-gateway/services.php index cdbc14b52..7e3ff7eca 100644 --- a/modules/ppcp-wc-gateway/services.php +++ b/modules/ppcp-wc-gateway/services.php @@ -336,7 +336,8 @@ return array( $signup_link_cache, $signup_link_ids, $pui_status_cache, - $dcc_status_cache + $dcc_status_cache, + $container->get( 'http.redirector' ) ); }, 'wcgateway.order-processor' => static function ( ContainerInterface $container ): OrderProcessor { diff --git a/modules/ppcp-wc-gateway/src/Settings/SettingsListener.php b/modules/ppcp-wc-gateway/src/Settings/SettingsListener.php index dac2dacf3..88c9bee98 100644 --- a/modules/ppcp-wc-gateway/src/Settings/SettingsListener.php +++ b/modules/ppcp-wc-gateway/src/Settings/SettingsListener.php @@ -13,6 +13,7 @@ use WooCommerce\PayPalCommerce\ApiClient\Authentication\Bearer; use WooCommerce\PayPalCommerce\ApiClient\Authentication\PayPalBearer; use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException; use WooCommerce\PayPalCommerce\ApiClient\Helper\Cache; +use WooCommerce\PayPalCommerce\Http\RedirectorInterface; use WooCommerce\PayPalCommerce\Onboarding\State; use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway; use WooCommerce\PayPalCommerce\WcGateway\Helper\DCCProductStatus; @@ -111,20 +112,28 @@ class SettingsListener { */ protected $dcc_status_cache; + /** + * The HTTP redirector. + * + * @var RedirectorInterface + */ + protected $redirector; + /** * SettingsListener constructor. * - * @param Settings $settings The settings. - * @param array $setting_fields The setting fields. - * @param WebhookRegistrar $webhook_registrar The Webhook Registrar. - * @param Cache $cache The Cache. - * @param State $state The state. - * @param Bearer $bearer The bearer. - * @param string $page_id ID of the current PPCP gateway settings page, or empty if it is not such page. - * @param Cache $signup_link_cache The signup link cache. - * @param array $signup_link_ids Signup link ids. - * @param Cache $pui_status_cache The PUI status cache. - * @param Cache $dcc_status_cache The DCC status cache. + * @param Settings $settings The settings. + * @param array $setting_fields The setting fields. + * @param WebhookRegistrar $webhook_registrar The Webhook Registrar. + * @param Cache $cache The Cache. + * @param State $state The state. + * @param Bearer $bearer The bearer. + * @param string $page_id ID of the current PPCP gateway settings page, or empty if it is not such page. + * @param Cache $signup_link_cache The signup link cache. + * @param array $signup_link_ids Signup link ids. + * @param Cache $pui_status_cache The PUI status cache. + * @param Cache $dcc_status_cache The DCC status cache. + * @param RedirectorInterface $redirector The HTTP redirector. */ public function __construct( Settings $settings, @@ -137,7 +146,8 @@ class SettingsListener { Cache $signup_link_cache, array $signup_link_ids, Cache $pui_status_cache, - Cache $dcc_status_cache + Cache $dcc_status_cache, + RedirectorInterface $redirector ) { $this->settings = $settings; @@ -151,6 +161,7 @@ class SettingsListener { $this->signup_link_ids = $signup_link_ids; $this->pui_status_cache = $pui_status_cache; $this->dcc_status_cache = $dcc_status_cache; + $this->redirector = $redirector; } /** @@ -198,8 +209,7 @@ class SettingsListener { $redirect_url = add_query_arg( 'ppcp-onboarding-error', '1', $redirect_url ); } - wp_safe_redirect( $redirect_url, 302 ); - exit; + $this->redirector->redirect( $redirect_url ); } /** @@ -337,8 +347,7 @@ class SettingsListener { } if ( $redirect_url ) { - wp_safe_redirect( $redirect_url, 302 ); - exit; + $this->redirector->redirect( $redirect_url ); } // phpcs:enable WordPress.Security.NonceVerification.Missing diff --git a/src/Http/RedirectorInterface.php b/src/Http/RedirectorInterface.php new file mode 100644 index 000000000..83403e496 --- /dev/null +++ b/src/Http/RedirectorInterface.php @@ -0,0 +1,22 @@ +getVersion(); }, + + 'http.redirector' => function( ContainerInterface $container ) : RedirectorInterface { + return new WpRedirector(); + }, ); diff --git a/tests/PHPUnit/Helper/RedirectorStub.php b/tests/PHPUnit/Helper/RedirectorStub.php new file mode 100644 index 000000000..9e79c6fe5 --- /dev/null +++ b/tests/PHPUnit/Helper/RedirectorStub.php @@ -0,0 +1,14 @@ + function () { + return new RedirectorStub(); + } + ], $overriddenServices); + $module = new class ($overriddenServices) implements ModuleInterface { public function __construct(array $services) { $this->services = $services; diff --git a/tests/PHPUnit/WcGateway/Settings/SettingsListenerTest.php b/tests/PHPUnit/WcGateway/Settings/SettingsListenerTest.php index 403217a1b..d1397a421 100644 --- a/tests/PHPUnit/WcGateway/Settings/SettingsListenerTest.php +++ b/tests/PHPUnit/WcGateway/Settings/SettingsListenerTest.php @@ -5,6 +5,8 @@ namespace WooCommerce\PayPalCommerce\WcGateway\Settings; use Requests_Utility_CaseInsensitiveDictionary; use WooCommerce\PayPalCommerce\ApiClient\Authentication\Bearer; use WooCommerce\PayPalCommerce\ApiClient\Helper\Cache; +use WooCommerce\PayPalCommerce\Helper\RedirectorStub; +use WooCommerce\PayPalCommerce\Helper\StubRedirectionException; use WooCommerce\PayPalCommerce\ModularTestCase; use WooCommerce\PayPalCommerce\Onboarding\State; use Mockery; @@ -50,7 +52,8 @@ class SettingsListenerTest extends ModularTestCase $signup_link_cache, $signup_link_ids, $pui_status_cache, - $dcc_status_cache + $dcc_status_cache, + new RedirectorStub() ); $_GET['section'] = PayPalGateway::ID; @@ -85,7 +88,7 @@ class SettingsListenerTest extends ModularTestCase $dcc_status_cache->shouldReceive('has') ->andReturn(false); - expect('wp_safe_redirect')->once(); + $this->expectException(StubRedirectionException::class); $testee->listen(); }