diff --git a/plugins/woocommerce/src/StoreApi/Routes/V1/Checkout.php b/plugins/woocommerce/src/StoreApi/Routes/V1/Checkout.php index 69e72e54806..6991ffeb025 100644 --- a/plugins/woocommerce/src/StoreApi/Routes/V1/Checkout.php +++ b/plugins/woocommerce/src/StoreApi/Routes/V1/Checkout.php @@ -201,6 +201,7 @@ class Checkout extends AbstractCartRoute { $address_fields = $this->additional_fields_controller->get_fields_for_location( 'address' ); if ( ! empty( $address_fields ) ) { + $needs_shipping = WC()->cart->needs_shipping(); foreach ( $address_fields as $field_key => $address_field ) { if ( $address_field['required'] && ! isset( $request['billing_address'][ $field_key ] ) ) { throw new RouteException( @@ -210,7 +211,7 @@ class Checkout extends AbstractCartRoute { 400 ); } - if ( $address_field['required'] && ! isset( $request['shipping_address'][ $field_key ] ) ) { + if ( $needs_shipping && $address_field['required'] && ! isset( $request['shipping_address'][ $field_key ] ) ) { throw new RouteException( 'woocommerce_rest_checkout_missing_required_field', /* translators: %s: is the field label */ diff --git a/plugins/woocommerce/tests/php/src/Blocks/StoreApi/Routes/AdditionalFields.php b/plugins/woocommerce/tests/php/src/Blocks/StoreApi/Routes/AdditionalFields.php index 8076f7ee69a..6352f7ba066 100644 --- a/plugins/woocommerce/tests/php/src/Blocks/StoreApi/Routes/AdditionalFields.php +++ b/plugins/woocommerce/tests/php/src/Blocks/StoreApi/Routes/AdditionalFields.php @@ -72,6 +72,24 @@ class AdditionalFields extends MockeryTestCase { 'weight' => 10, ) ), + $fixtures->get_simple_product( + array( + 'name' => 'Virtual Test Product 3', + 'stock_status' => 'instock', + 'regular_price' => 10, + 'weight' => 10, + 'virtual' => true, + ) + ), + $fixtures->get_simple_product( + array( + 'name' => 'Downloadable Test Product 4', + 'stock_status' => 'instock', + 'regular_price' => 10, + 'weight' => 10, + 'downloadable' => true, + ) + ), ); $this->reset_session(); } @@ -1682,6 +1700,91 @@ class AdditionalFields extends MockeryTestCase { $this->assertFalse( $this->controller->is_field( $id ), \sprintf( '%s is still registered', $id ) ); } + /** + * Ensures an order for a virtual product can be placed without a shipping address, but an order for a downloadable + * non-virtual cannot. + */ + public function test_place_virtual_downloadable_product_order() { + WC()->cart->empty_cart(); + WC()->cart->add_to_cart( $this->products[2]->get_id(), 2 ); + $id = 'plugin-namespace/my-required-field'; + $label = 'My Required Field'; + \woocommerce_register_additional_checkout_field( + array( + 'id' => $id, + 'label' => $label, + 'location' => 'address', + 'type' => 'text', + 'required' => true, + ) + ); + + $request = new \WP_REST_Request( 'POST', '/wc/store/v1/checkout' ); + $request->set_header( 'Nonce', wp_create_nonce( 'wc_store_api' ) ); + $request->set_body_params( + array( + 'billing_address' => (object) array( + 'first_name' => 'test', + 'last_name' => 'test', + 'company' => '', + 'address_1' => 'test', + 'address_2' => '', + 'city' => 'test', + 'state' => '', + 'postcode' => 'cb241ab', + 'country' => 'GB', + 'phone' => '', + 'email' => 'testaccount@test.com', + 'plugin-namespace/gov-id' => 'gov id', + 'plugin-namespace/my-required-field' => 'req. field', + ), + 'payment_method' => 'bacs', + 'additional_fields' => array( + 'plugin-namespace/job-function' => 'engineering', + ), + ) + ); + $response = rest_get_server()->dispatch( $request ); + $data = $response->get_data(); + $this->assertEquals( 200, $response->get_status(), print_r( $data, true ) ); + + // Test with downloadable, but not virtual product. + WC()->cart->empty_cart(); + WC()->cart->add_to_cart( $this->products[3]->get_id(), 2 ); + + $request = new \WP_REST_Request( 'POST', '/wc/store/v1/checkout' ); + $request->set_header( 'Nonce', wp_create_nonce( 'wc_store_api' ) ); + $request->set_body_params( + array( + 'billing_address' => (object) array( + 'first_name' => 'test', + 'last_name' => 'test', + 'company' => '', + 'address_1' => 'test', + 'address_2' => '', + 'city' => 'test', + 'state' => '', + 'postcode' => 'cb241ab', + 'country' => 'GB', + 'phone' => '', + 'email' => 'testaccount@test.com', + 'plugin-namespace/gov-id' => 'gov id', + 'plugin-namespace/my-required-field' => 'req. field', + ), + 'payment_method' => 'bacs', + 'additional_fields' => array( + 'plugin-namespace/job-function' => 'engineering', + ), + ) + ); + $response = rest_get_server()->dispatch( $request ); + $data = $response->get_data(); + + // The product is downloadable, but not virtual, so should still require a shipping address. + $this->assertEquals( 400, $response->get_status(), print_r( $data, true ) ); + $this->assertEquals( 'There was a problem with the provided shipping address: Government ID is required', $data['message'], print_r( $data, true ) ); + } + /** * Ensures an error is returned when required fields in Contact are missing. */