Commit 0b68089427 for woocommerce

commit 0b68089427b7961a7a05002cebc760a98961f539
Author: Vlad Olaru <vlad.olaru@automattic.com>
Date:   Mon Nov 3 09:00:36 2025 +0200

    [Payments NOX] Prevent WC core-provided gateways showing deactivation actions (#61700)

    * Core payment provider should not return a plugin file path

    * test: Add core payment provider tests to ensure no plugin file

    * Add changelog

    * test: Fix payments providers integration tests

    ---------

    Co-authored-by: Cvetan Cvetanov <cvetan.cvetanov@automattic.com>

diff --git a/plugins/woocommerce/changelog/fix-WOOPLUG-5598-nox-wc-core-gateways-deactivation b/plugins/woocommerce/changelog/fix-WOOPLUG-5598-nox-wc-core-gateways-deactivation
new file mode 100644
index 0000000000..79937d9109
--- /dev/null
+++ b/plugins/woocommerce/changelog/fix-WOOPLUG-5598-nox-wc-core-gateways-deactivation
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Prevent core-provided payment gateways from showing deactivation actions on the Payments settings page.
diff --git a/plugins/woocommerce/src/Internal/Admin/Settings/PaymentsProviders/WCCore.php b/plugins/woocommerce/src/Internal/Admin/Settings/PaymentsProviders/WCCore.php
index 40bd04f766..929d758e30 100644
--- a/plugins/woocommerce/src/Internal/Admin/Settings/PaymentsProviders/WCCore.php
+++ b/plugins/woocommerce/src/Internal/Admin/Settings/PaymentsProviders/WCCore.php
@@ -91,4 +91,21 @@ class WCCore extends PaymentGateway {

 		return parent::is_in_test_mode_onboarding( $payment_gateway );
 	}
+
+	/**
+	 * Get the plugin details for a WC core-provided payment gateway.
+	 *
+	 * @param WC_Payment_Gateway $payment_gateway The payment gateway object.
+	 *
+	 * @return array The plugin details for the payment gateway.
+	 */
+	public function get_plugin_details( WC_Payment_Gateway $payment_gateway ): array {
+		$plugin_details = parent::get_plugin_details( $payment_gateway );
+
+		// Since these are core-provided gateways, we need to make sure that the provider (WC) can't be deactivated.
+		// The way to do this is to NOT provide a plugin file path.
+		$plugin_details['file'] = '';
+
+		return $plugin_details;
+	}
 }
diff --git a/plugins/woocommerce/tests/php/src/Internal/Admin/Settings/PaymentsProviders/WCCoreTest.php b/plugins/woocommerce/tests/php/src/Internal/Admin/Settings/PaymentsProviders/WCCoreTest.php
index f6e470380e..b3b52527be 100644
--- a/plugins/woocommerce/tests/php/src/Internal/Admin/Settings/PaymentsProviders/WCCoreTest.php
+++ b/plugins/woocommerce/tests/php/src/Internal/Admin/Settings/PaymentsProviders/WCCoreTest.php
@@ -32,6 +32,54 @@ class WCCoreTest extends WC_Unit_Test_Case {
 		$this->sut = new WCCore();
 	}

+	/**
+	 * Data provider for core gateway IDs.
+	 *
+	 * @return array<string, array<string, string>>
+	 */
+	public function provider_core_gateway_ids(): array {
+		return array(
+			'BACS'   => array( 'gateway_id' => WC_Gateway_BACS::ID ),
+			'Cheque' => array( 'gateway_id' => WC_Gateway_Cheque::ID ),
+			'COD'    => array( 'gateway_id' => WC_Gateway_COD::ID ),
+			'PayPal' => array( 'gateway_id' => WC_Gateway_Paypal::ID ),
+		);
+	}
+
+	/**
+	 * Test get_plugin_details returns empty file path to prevent deactivation.
+	 *
+	 * @dataProvider provider_core_gateway_ids
+	 *
+	 * @param string $gateway_id The gateway ID to test.
+	 */
+	public function test_get_plugin_details_prevents_deactivation( string $gateway_id ) {
+		// Arrange.
+		$fake_gateway = new FakePaymentGateway(
+			$gateway_id,
+			array(
+				'enabled'            => true,
+				'plugin_slug'        => 'woocommerce',
+				'plugin_file'        => 'woocommerce/woocommerce.php',
+				'method_title'       => 'Test Gateway',
+				'method_description' => 'Test gateway description.',
+			),
+		);
+
+		// Act.
+		$plugin_details = $this->sut->get_plugin_details( $fake_gateway );
+
+		// Assert - Core gateways should have empty file path to prevent deactivation.
+		$this->assertIsArray( $plugin_details );
+		$this->assertArrayHasKey( 'file', $plugin_details );
+		$this->assertSame( '', $plugin_details['file'], "Gateway $gateway_id should have empty file path for the plugin details." );
+
+		// Assert - Other expected keys should still be present.
+		$this->assertArrayHasKey( '_type', $plugin_details );
+		$this->assertArrayHasKey( 'slug', $plugin_details );
+		$this->assertArrayHasKey( 'status', $plugin_details );
+	}
+
 	/**
 	 * Test get_icon.
 	 */
diff --git a/plugins/woocommerce/tests/php/src/Internal/Admin/Settings/PaymentsRestControllerIntegrationTest.php b/plugins/woocommerce/tests/php/src/Internal/Admin/Settings/PaymentsRestControllerIntegrationTest.php
index 52b49bc29a..1c63d4d3b8 100644
--- a/plugins/woocommerce/tests/php/src/Internal/Admin/Settings/PaymentsRestControllerIntegrationTest.php
+++ b/plugins/woocommerce/tests/php/src/Internal/Admin/Settings/PaymentsRestControllerIntegrationTest.php
@@ -573,7 +573,7 @@ class PaymentsRestControllerIntegrationTest extends WC_REST_Unit_Test_Case {
 		$this->assertArrayHasKey( 'status', $offline_pms_group['plugin'], 'Provider (offline payment methods group) `plugin[status]` entry is missing' );
 		$this->assertSame( PaymentsProviders::EXTENSION_ACTIVE, $offline_pms_group['plugin']['status'] );

-		// Assert that the PayPal gateway is returned as enabled.
+		// Assert that the PayPal Standard gateway is returned as enabled.
 		$provider = $data['providers'][3];
 		$this->assertTrue( $provider['state']['enabled'] );
 		// Assert that the PayPal gateway has all the details.
@@ -587,9 +587,13 @@ class PaymentsRestControllerIntegrationTest extends WC_REST_Unit_Test_Case {
 		$this->assertIsList( $provider['supports'], 'Provider (gateway) `supports` entry is not a list' );
 		$this->assertArrayHasKey( 'plugin', $provider, 'Provider (gateway) `plugin` entry is missing' );
 		$this->assertArrayHasKey( '_type', $provider['plugin'], 'Provider (gateway) `plugin[_type]` entry is missing' );
+		$this->assertSame( PaymentsProviders::EXTENSION_TYPE_WPORG, $provider['plugin']['_type'], 'PayPal Standard gateway `plugin[_type]` entry is not `' . PaymentsProviders::EXTENSION_TYPE_WPORG . '`' );
 		$this->assertArrayHasKey( 'slug', $provider['plugin'], 'Provider (gateway) `plugin[slug]` entry is missing' );
+		$this->assertSame( 'woocommerce', $provider['plugin']['slug'] );
 		$this->assertArrayHasKey( 'file', $provider['plugin'], 'Provider (gateway) `plugin[file]` entry is missing' );
+		$this->assertSame( '', $provider['plugin']['file'] ); // Always empty since it's part of WooCommerce core and we don't want to allow deactivation.
 		$this->assertArrayHasKey( 'status', $provider['plugin'], 'Provider (gateway) `plugin[status]` entry is missing' );
+		$this->assertSame( PaymentsProviders::EXTENSION_ACTIVE, $provider['plugin']['status'], 'PayPal Standard gateway `plugin[status]` entry is not `' . PaymentsProviders::EXTENSION_ACTIVE . '`' );
 		$this->assertArrayHasKey( 'links', $provider, 'Provider (gateway) `links` entry is missing' );
 		$this->assertCount( 1, $provider['links'] );
 		$this->assertArrayHasKey( 'state', $provider, 'Provider (gateway) `state` entry is missing' );
@@ -630,7 +634,7 @@ class PaymentsRestControllerIntegrationTest extends WC_REST_Unit_Test_Case {
 		$this->assertArrayHasKey( 'slug', $offline_pm['plugin'], 'Offline payment method `plugin[slug]` entry is missing' );
 		$this->assertSame( 'woocommerce', $offline_pm['plugin']['slug'] );
 		$this->assertArrayHasKey( 'file', $offline_pm['plugin'], 'Offline payment method `plugin[file]` entry is missing' );
-		$this->assertSame( 'woocommerce/woocommerce', $offline_pm['plugin']['file'] ); // Skips the .php extension.
+		$this->assertSame( '', $offline_pm['plugin']['file'] ); // Always empty since it's part of WooCommerce core and we don't want to allow deactivation.
 		$this->assertArrayHasKey( 'status', $offline_pm['plugin'], 'Offline payment method `plugin[status]` entry is missing' );
 		$this->assertSame( PaymentsProviders::EXTENSION_ACTIVE, $offline_pm['plugin']['status'] );
 		$this->assertArrayHasKey( 'management', $offline_pm, 'Offline payment method `management` entry is missing' );