Skip to content
This repository has been archived by the owner on Feb 23, 2024. It is now read-only.

Commit

Permalink
Unset default customer state if it doesn't match country (#8460)
Browse files Browse the repository at this point in the history
* Unset default state

* add controller for customers

* rename validation file

* explain fix inline

* address feedback

* revert back state logic

* Update src/StoreApi/Utilities/ValidationUtils.php

Co-authored-by: Mike Jolley <mike.jolley@me.com>

---------

Co-authored-by: Mike Jolley <mike.jolley@me.com>
  • Loading branch information
2 people authored and tarhi-saad committed Feb 17, 2023
1 parent 72cd6d2 commit d2c331d
Show file tree
Hide file tree
Showing 5 changed files with 93 additions and 61 deletions.
18 changes: 16 additions & 2 deletions src/StoreApi/Routes/V1/CartUpdateCustomer.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
namespace Automattic\WooCommerce\StoreApi\Routes\V1;

use Automattic\WooCommerce\StoreApi\Utilities\DraftOrderTrait;
use Automattic\WooCommerce\StoreApi\Utilities\ValidationUtils;

/**
* CartUpdateCustomer class.
Expand Down Expand Up @@ -215,16 +216,29 @@ protected function get_route_post_response( \WP_REST_Request $request ) {
* @return array
*/
protected function get_customer_billing_address( \WC_Customer $customer ) {
$validation_util = new ValidationUtils();
$billing_country = $customer->get_billing_country();
$billing_state = $customer->get_billing_state();

/**
* There's a bug in WooCommerce core in which not having a state ("") would result in us validating against the store's state.
* This resets the state to an empty string if it doesn't match the country.
*
* @todo Removing this handling once we fix the issue with the state value always being the store one.
*/
if ( ! $validation_util->validate_state( $billing_state, $billing_country ) ) {
$billing_state = '';
}
return [
'first_name' => $customer->get_billing_first_name(),
'last_name' => $customer->get_billing_last_name(),
'company' => $customer->get_billing_company(),
'address_1' => $customer->get_billing_address_1(),
'address_2' => $customer->get_billing_address_2(),
'city' => $customer->get_billing_city(),
'state' => $customer->get_billing_state(),
'state' => $billing_state,
'postcode' => $customer->get_billing_postcode(),
'country' => $customer->get_billing_country(),
'country' => $billing_country,
'phone' => $customer->get_billing_phone(),
'email' => $customer->get_billing_email(),
];
Expand Down
67 changes: 10 additions & 57 deletions src/StoreApi/Schemas/V1/AbstractAddressSchema.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
<?php
namespace Automattic\WooCommerce\StoreApi\Schemas\V1;

use Automattic\WooCommerce\StoreApi\Utilities\ValidationUtils;

/**
* AddressSchema class.
*
Expand Down Expand Up @@ -87,6 +89,8 @@ public function get_properties() {
* @return array
*/
public function sanitize_callback( $address, $request, $param ) {
$validation_util = new ValidationUtils();

$address = array_merge( array_fill_keys( array_keys( $this->get_properties() ), '' ), (array) $address );
$address['country'] = wc_strtoupper( wc_clean( wp_unslash( $address['country'] ) ) );
$address['first_name'] = wc_clean( wp_unslash( $address['first_name'] ) );
Expand All @@ -95,64 +99,12 @@ public function sanitize_callback( $address, $request, $param ) {
$address['address_1'] = wc_clean( wp_unslash( $address['address_1'] ) );
$address['address_2'] = wc_clean( wp_unslash( $address['address_2'] ) );
$address['city'] = wc_clean( wp_unslash( $address['city'] ) );
$address['state'] = $this->format_state( wc_clean( wp_unslash( $address['state'] ) ), $address['country'] );
$address['state'] = $validation_util->format_state( wc_clean( wp_unslash( $address['state'] ) ), $address['country'] );
$address['postcode'] = $address['postcode'] ? wc_format_postcode( wc_clean( wp_unslash( $address['postcode'] ) ), $address['country'] ) : '';
$address['phone'] = wc_clean( wp_unslash( $address['phone'] ) );
return $address;
}

/**
* Get list of states for a country.
*
* @param string $country Country code.
* @return array Array of state names indexed by state keys.
*/
protected function get_states_for_country( $country ) {
return $country ? array_filter( (array) \wc()->countries->get_states( $country ) ) : [];
}

/**
* Validate provided state against a countries list of defined states.
*
* If there are no defined states for a country, any given state is valid.
*
* @param string $state State name or code (sanitized).
* @param string $country Country code.
* @return boolean Valid or not valid.
*/
protected function validate_state( $state, $country ) {
$states = $this->get_states_for_country( $country );

if ( count( $states ) && ! in_array( \wc_strtoupper( $state ), array_map( '\wc_strtoupper', array_keys( $states ) ), true ) ) {
return false;
}

return true;
}

/**
* Format a state based on the country. If country has defined states, will return a valid upper case state code.
*
* @param string $state State name or code (sanitized).
* @param string $country Country code.
* @return string
*/
protected function format_state( $state, $country ) {
$states = $this->get_states_for_country( $country );

if ( count( $states ) ) {
$state = \wc_strtoupper( $state );
$state_values = array_map( 'wc_strtoupper', array_flip( array_map( '\wc_strtoupper', $states ) ) );

if ( isset( $state_values[ $state ] ) ) {
// Convert to state code if a state name was provided.
return $state_values[ $state ];
}
}

return $state;
}

/**
* Validate the given address object.
*
Expand All @@ -164,8 +116,9 @@ protected function format_state( $state, $country ) {
* @return true|\WP_Error
*/
public function validate_callback( $address, $request, $param ) {
$errors = new \WP_Error();
$address = $this->sanitize_callback( $address, $request, $param );
$errors = new \WP_Error();
$address = $this->sanitize_callback( $address, $request, $param );
$validation_util = new ValidationUtils();

if ( ! empty( $address['country'] ) && ! in_array( $address['country'], array_keys( wc()->countries->get_countries() ), true ) ) {
$errors->add(
Expand All @@ -179,14 +132,14 @@ public function validate_callback( $address, $request, $param ) {
return $errors;
}

if ( ! empty( $address['state'] ) && ! $this->validate_state( $address['state'], $address['country'] ) ) {
if ( ! empty( $address['state'] ) && ! $validation_util->validate_state( $address['state'], $address['country'] ) ) {
$errors->add(
'invalid_state',
sprintf(
/* translators: %1$s given state, %2$s valid states */
__( 'The provided state (%1$s) is not valid. Must be one of: %2$s', 'woo-gutenberg-products-block' ),
esc_html( $address['state'] ),
implode( ', ', array_keys( $this->get_states_for_country( $address['country'] ) ) )
implode( ', ', array_keys( $validation_util->get_states_for_country( $address['country'] ) ) )
)
);
}
Expand Down
4 changes: 3 additions & 1 deletion src/StoreApi/Schemas/V1/BillingAddressSchema.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
namespace Automattic\WooCommerce\StoreApi\Schemas\V1;

use Automattic\WooCommerce\StoreApi\Exceptions\RouteException;
use Automattic\WooCommerce\StoreApi\Utilities\ValidationUtils;

/**
* BillingAddressSchema class.
Expand Down Expand Up @@ -89,11 +90,12 @@ public function validate_callback( $address, $request, $param ) {
* @return stdClass
*/
public function get_item_response( $address ) {
$validation_util = new ValidationUtils();
if ( ( $address instanceof \WC_Customer || $address instanceof \WC_Order ) ) {
$billing_country = $address->get_billing_country();
$billing_state = $address->get_billing_state();

if ( ! $this->validate_state( $billing_state, $billing_country ) ) {
if ( ! $validation_util->validate_state( $billing_state, $billing_country ) ) {
$billing_state = '';
}

Expand Down
4 changes: 3 additions & 1 deletion src/StoreApi/Schemas/V1/ShippingAddressSchema.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
namespace Automattic\WooCommerce\StoreApi\Schemas\V1;

use Automattic\WooCommerce\StoreApi\Exceptions\RouteException;
use Automattic\WooCommerce\StoreApi\Utilities\ValidationUtils;

/**
* ShippingAddressSchema class.
Expand Down Expand Up @@ -32,11 +33,12 @@ class ShippingAddressSchema extends AbstractAddressSchema {
* @return stdClass
*/
public function get_item_response( $address ) {
$validation_util = new ValidationUtils();
if ( ( $address instanceof \WC_Customer || $address instanceof \WC_Order ) ) {
$shipping_country = $address->get_shipping_country();
$shipping_state = $address->get_shipping_state();

if ( ! $this->validate_state( $shipping_state, $shipping_country ) ) {
if ( ! $validation_util->validate_state( $shipping_state, $shipping_country ) ) {
$shipping_state = '';
}

Expand Down
61 changes: 61 additions & 0 deletions src/StoreApi/Utilities/ValidationUtils.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<?php
namespace Automattic\WooCommerce\StoreApi\Utilities;

/**
* ValidationUtils class.
* Helper class which validates and update customer info.
*/
class ValidationUtils {
/**
* Get list of states for a country.
*
* @param string $country Country code.
* @return array Array of state names indexed by state keys.
*/
public function get_states_for_country( $country ) {
return $country ? array_filter( (array) \wc()->countries->get_states( $country ) ) : [];
}

/**
* Validate provided state against a countries list of defined states.
*
* If there are no defined states for a country, any given state is valid.
*
* @param string $state State name or code (sanitized).
* @param string $country Country code.
* @return boolean Valid or not valid.
*/
public function validate_state( $state, $country ) {
$states = $this->get_states_for_country( $country );

if ( count( $states ) && ! in_array( \wc_strtoupper( $state ), array_map( '\wc_strtoupper', array_keys( $states ) ), true ) ) {
return false;
}

return true;
}


/**
* Format a state based on the country. If country has defined states, will return a valid upper case state code.
*
* @param string $state State name or code (sanitized).
* @param string $country Country code.
* @return string
*/
public function format_state( $state, $country ) {
$states = $this->get_states_for_country( $country );

if ( count( $states ) ) {
$state = \wc_strtoupper( $state );
$state_values = array_map( '\wc_strtoupper', array_flip( array_map( '\wc_strtoupper', $states ) ) );

if ( isset( $state_values[ $state ] ) ) {
// Convert to state code if a state name was provided.
return $state_values[ $state ];
}
}

return $state;
}
}

0 comments on commit d2c331d

Please sign in to comment.