Commit 3de8f28567c for woocommerce
commit 3de8f28567cfb6f8b1ef2a0218d349e813d707fe
Author: Jorge A. Torres <jorge.torres@automattic.com>
Date: Wed Aug 26 17:32:18 2026 +0100
Fix hidden address fields being required in the classic checkout (#67706)
diff --git a/plugins/woocommerce/changelog/65412-fix-hidden-required-fields b/plugins/woocommerce/changelog/65412-fix-hidden-required-fields
new file mode 100644
index 00000000000..77ba9462215
--- /dev/null
+++ b/plugins/woocommerce/changelog/65412-fix-hidden-required-fields
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Skip required validation for hidden address fields on the classic checkout and My Account address form.
diff --git a/plugins/woocommerce/includes/class-wc-checkout.php b/plugins/woocommerce/includes/class-wc-checkout.php
index 6e5102b5eed..1483ef4dc01 100644
--- a/plugins/woocommerce/includes/class-wc-checkout.php
+++ b/plugins/woocommerce/includes/class-wc-checkout.php
@@ -907,7 +907,7 @@ class WC_Checkout {
if ( ! isset( $data[ $key ] ) ) {
continue;
}
- $required = ! empty( $field['required'] );
+ $required = ! empty( $field['required'] ) && empty( $field['hidden'] );
$format = array_filter( isset( $field['validate'] ) ? (array) $field['validate'] : array() );
$field_label = isset( $field['label'] ) ? $field['label'] : '';
diff --git a/plugins/woocommerce/includes/class-wc-countries.php b/plugins/woocommerce/includes/class-wc-countries.php
index bc174fe4dca..c87df90ec5d 100644
--- a/plugins/woocommerce/includes/class-wc-countries.php
+++ b/plugins/woocommerce/includes/class-wc-countries.php
@@ -1752,6 +1752,13 @@ class WC_Countries {
$locale = $this->get_country_locale();
if ( isset( $locale[ $country ] ) ) {
+ // wc_array_overlay() only copies keys that exist in the base array, so seed 'hidden' where the locale defines it.
+ foreach ( $locale[ $country ] as $field_key => $locale_field ) {
+ if ( isset( $fields[ $field_key ], $locale_field['hidden'] ) && is_array( $fields[ $field_key ] ) ) {
+ $fields[ $field_key ]['hidden'] = false;
+ }
+ }
+
$fields = wc_array_overlay( $fields, $locale[ $country ] );
}
diff --git a/plugins/woocommerce/includes/class-wc-form-handler.php b/plugins/woocommerce/includes/class-wc-form-handler.php
index a189b36db97..d945c5a5b57 100644
--- a/plugins/woocommerce/includes/class-wc-form-handler.php
+++ b/plugins/woocommerce/includes/class-wc-form-handler.php
@@ -195,7 +195,7 @@ class WC_Form_Handler {
$value = apply_filters( 'woocommerce_process_myaccount_field_' . $key, $value );
// Validation: Required fields.
- if ( ! empty( $field['required'] ) && empty( $value ) ) {
+ if ( ! empty( $field['required'] ) && empty( $field['hidden'] ) && empty( $value ) ) {
/* translators: %s: Field name. */
wc_add_notice( sprintf( __( '%s is a required field.', 'woocommerce' ), $field['label'] ), 'error', array( 'id' => $key ) );
}
diff --git a/plugins/woocommerce/tests/php/includes/class-wc-checkout-test.php b/plugins/woocommerce/tests/php/includes/class-wc-checkout-test.php
index 63efd1b575e..9517be8f4cd 100644
--- a/plugins/woocommerce/tests/php/includes/class-wc-checkout-test.php
+++ b/plugins/woocommerce/tests/php/includes/class-wc-checkout-test.php
@@ -51,6 +51,7 @@ class WC_Checkout_Test extends \WC_Unit_Test_Case {
public function tearDown(): void {
remove_filter( 'woocommerce_checkout_registration_enabled', '__return_true' );
delete_option( 'woocommerce_calc_taxes' );
+ WC()->countries->locale = array();
foreach ( $this->extra_field_filters as $extra_field_filter ) {
remove_filter( 'woocommerce_checkout_fields', $extra_field_filter );
@@ -215,6 +216,40 @@ class WC_Checkout_Test extends \WC_Unit_Test_Case {
$this->assertEmpty( $errors->get_error_message( 'shipping_country_validation' ) );
}
+ /**
+ * @testdox 'validate_posted_data' skips the required check for fields hidden via the country locale.
+ *
+ * @testWith [true]
+ * [false]
+ *
+ * @param bool $hidden Whether the locale marks the postcode field as hidden.
+ */
+ public function test_validate_posted_data_skips_required_check_for_hidden_fields( $hidden ) {
+ $locale_filter = function ( $locale ) use ( $hidden ) {
+ $locale['ES']['postcode']['hidden'] = $hidden;
+ return $locale;
+ };
+ add_filter( 'woocommerce_get_country_locale', $locale_filter );
+ WC()->countries->locale = array();
+ $_POST['billing_country'] = 'ES';
+
+ $data = array(
+ 'billing_country' => 'ES',
+ 'billing_postcode' => '',
+ 'ship_to_different_address' => false,
+ );
+ $errors = new WP_Error();
+
+ $this->sut->validate_posted_data( $data, $errors );
+
+ $required_error = $errors->get_error_message( 'billing_postcode_required' );
+ if ( $hidden ) {
+ $this->assertEmpty( $required_error, 'Hidden fields should not trigger a required-field error.' );
+ } else {
+ $this->assertNotEmpty( $required_error, 'Visible required fields should still trigger a required-field error.' );
+ }
+ }
+
/**
* @testdox 'validate_posted_data' reports a required field error, and doesn't throw, for an empty phone field in a fieldset without a country.
*
diff --git a/plugins/woocommerce/tests/php/includes/class-wc-countries-test.php b/plugins/woocommerce/tests/php/includes/class-wc-countries-test.php
index d1cb705812f..37a8a984c25 100644
--- a/plugins/woocommerce/tests/php/includes/class-wc-countries-test.php
+++ b/plugins/woocommerce/tests/php/includes/class-wc-countries-test.php
@@ -18,6 +18,40 @@ class WC_Countries_Test extends \WC_Unit_Test_Case {
$this->assertEquals( $expected, wc()->countries->get_country_from_alpha_3_code( $country_code ) );
}
+ /**
+ * @testdox 'get_address_fields' carries a locale-only 'hidden' flag through the merge with the default fields.
+ *
+ * @testWith [true]
+ * [false]
+ *
+ * @param bool $hidden Value of the 'hidden' flag set via the locale filter.
+ * @return void
+ */
+ public function test_get_address_fields_includes_hidden_flag_from_locale( $hidden ) {
+ $locale_filter = function ( $locale ) use ( $hidden ) {
+ $locale['ES']['postcode']['hidden'] = $hidden;
+ return $locale;
+ };
+ add_filter( 'woocommerce_get_country_locale', $locale_filter );
+
+ $fields = ( new WC_Countries() )->get_address_fields( 'ES', 'billing_' );
+
+ remove_filter( 'woocommerce_get_country_locale', $locale_filter );
+
+ $this->assertSame( $hidden, $fields['billing_postcode']['hidden'], 'The locale hidden flag should survive the merge with the default fields.' );
+ }
+
+ /**
+ * @testdox 'get_address_fields' does not add a 'hidden' key when the locale does not define one.
+ *
+ * @return void
+ */
+ public function test_get_address_fields_has_no_hidden_key_when_locale_does_not_define_one() {
+ $fields = ( new WC_Countries() )->get_address_fields( 'ES', 'billing_' );
+
+ $this->assertArrayNotHasKey( 'hidden', $fields['billing_postcode'] );
+ }
+
/**
* Provider for `test_get_country_from_alpha_3_code`.
*