Commit 08c8543421 for woocommerce

commit 08c854342137340aaec7d462a5845195d4e6208e
Author: Vlad Olaru <vlad.olaru@automattic.com>
Date:   Thu May 29 16:28:28 2025 +0300

    [Payments NOX] Allow payment gateway IDs with uppercase characters (#58382)

    * Allow uppercase characters in provide IDs

    * test: Add more unit tests for gateway order update and provider ID arg checks

    * Add changelog

diff --git a/plugins/woocommerce/changelog/fix-WOOPLUG-4425-payment-gateway-with-non-standard-ids b/plugins/woocommerce/changelog/fix-WOOPLUG-4425-payment-gateway-with-non-standard-ids
new file mode 100644
index 0000000000..73d6b73a24
--- /dev/null
+++ b/plugins/woocommerce/changelog/fix-WOOPLUG-4425-payment-gateway-with-non-standard-ids
@@ -0,0 +1,5 @@
+Significance: patch
+Type: fix
+Comment: Allow gateway IDs with uppercase characters in the new Payments Settings page UX.
+
+
diff --git a/plugins/woocommerce/src/Internal/Admin/Settings/PaymentsRestController.php b/plugins/woocommerce/src/Internal/Admin/Settings/PaymentsRestController.php
index f919a29d4a..0fd1bb79ad 100644
--- a/plugins/woocommerce/src/Internal/Admin/Settings/PaymentsRestController.php
+++ b/plugins/woocommerce/src/Internal/Admin/Settings/PaymentsRestController.php
@@ -414,8 +414,8 @@ class PaymentsRestController extends RestApiControllerBase {
 				return new WP_Error( 'rest_invalid_param', esc_html__( 'The ordering argument must be an object with provider IDs as keys and numeric values as values.', 'woocommerce' ), array( 'status' => 400 ) );
 			}

-			if ( sanitize_key( $provider_id ) !== $provider_id ) {
-				return new WP_Error( 'rest_invalid_param', esc_html__( 'The provider ID must be a valid string.', 'woocommerce' ), array( 'status' => 400 ) );
+			if ( $this->sanitize_provider_id( $provider_id ) !== $provider_id ) {
+				return new WP_Error( 'rest_invalid_param', esc_html__( 'The provider ID must be a string with only ASCII letters, digits, underscores, and dashes.', 'woocommerce' ), array( 'status' => 400 ) );
 			}

 			if ( false === filter_var( $order, FILTER_VALIDATE_INT ) ) {
@@ -436,13 +436,38 @@ class PaymentsRestController extends RestApiControllerBase {
 	private function sanitize_providers_order_arg( array $value ): array {
 		// Sanitize the ordering object to ensure that the order values are integers and the provider IDs are safe strings.
 		foreach ( $value as $provider_id => $order ) {
-			$id           = sanitize_key( $provider_id );
+			$id           = $this->sanitize_provider_id( $provider_id );
 			$value[ $id ] = intval( $order );
 		}

 		return $value;
 	}

+	/**
+	 * Sanitize a provider ID.
+	 *
+	 * This method ensures that the provider ID is a safe string by removing any unwanted characters.
+	 * It strips all HTML tags, removes accents, percent-encoded characters, and HTML entities,
+	 * and allows only lowercase and uppercase letters, digits, underscores, and dashes.
+	 *
+	 * @param string $provider_id The provider ID to sanitize.
+	 *
+	 * @return string The sanitized provider ID.
+	 */
+	private function sanitize_provider_id( string $provider_id ): string {
+		$provider_id = wp_strip_all_tags( $provider_id );
+		$provider_id = remove_accents( $provider_id );
+		// Remove percent-encoded characters.
+		$provider_id = preg_replace( '|%([a-fA-F0-9][a-fA-F0-9])|', '', $provider_id );
+		// Remove HTML entities.
+		$provider_id = preg_replace( '/&.+?;/', '', $provider_id );
+
+		// Only lowercase and uppercase ASCII letters, digits, underscores, and dashes are allowed.
+		$provider_id = preg_replace( '|[^a-z0-9_\-]|i', '', $provider_id );
+
+		return $provider_id;
+	}
+
 	/**
 	 * Prepare the response for the GET payment providers request.
 	 *
diff --git a/plugins/woocommerce/tests/php/src/Internal/Admin/Settings/PaymentsRestControllerTest.php b/plugins/woocommerce/tests/php/src/Internal/Admin/Settings/PaymentsRestControllerTest.php
index cc1628d8ed..91340697ee 100644
--- a/plugins/woocommerce/tests/php/src/Internal/Admin/Settings/PaymentsRestControllerTest.php
+++ b/plugins/woocommerce/tests/php/src/Internal/Admin/Settings/PaymentsRestControllerTest.php
@@ -551,7 +551,18 @@ class PaymentsRestControllerTest extends WC_REST_Unit_Test_Case {
 			array( array( WC_Gateway_Paypal::ID => false ) ),
 			array( array( WC_Gateway_Paypal::ID => 'bogus' ) ),
 			array( array( WC_Gateway_Paypal::ID => '1.0' ) ),
-			array( array( '()/paypal%#' => 1 ) ),
+			array( array( 'pay@pal' => 1 ) ), // Invalid provider ID with not allowed characters.
+			array( array( 'pay/pal' => 1 ) ), // Invalid provider ID with not allowed characters.
+			array( array( 'pay(pal' => 1 ) ), // Invalid provider ID with not allowed characters.
+			array( array( 'pay)pal' => 1 ) ), // Invalid provider ID with not allowed characters.
+			array( array( 'pay&pal' => 1 ) ), // Invalid provider ID with not allowed characters.
+			array( array( 'pay$pal' => 1 ) ), // Invalid provider ID with not allowed characters.
+			array( array( 'pay#pal' => 1 ) ), // Invalid provider ID with not allowed characters.
+			array( array( 'páypäl' => 1 ) ), // Invalid provider ID with accented characters.
+			array( array( '<script></script>paypal<a></a>' => 1 ) ), // Invalid provider ID with HTML tags.
+			array( array( '&nbsp;paypal&lt;' => 1 ) ), // Invalid provider ID with HTML entities.
+			array( array( 'pay&#60;pal' => 1 ) ), // Invalid provider ID with HTML entities.
+			array( array( '%2Fpay%3Apal' => 1 ) ), // Invalid provider ID with percent encoded characters.
 			array(
 				array(
 					WC_Gateway_Paypal::ID     => '1.1',
@@ -605,7 +616,7 @@ class PaymentsRestControllerTest extends WC_REST_Unit_Test_Case {
 		$request->set_body_params(
 			array(
 				'order_map' => array(
-					'provider1' => 1,
+					'Provider_01-1_AHA' => 1, // uppercase characters are allowed.
 				),
 			)
 		);