Edit File by line
/home/zeestwma/richards.../wp-conte.../plugins/woocomme.../src/StoreApi/Utilitie...
File: OrderController.php
<?php
[0] Fix | Delete
declare( strict_types = 1 );
[1] Fix | Delete
namespace Automattic\WooCommerce\StoreApi\Utilities;
[2] Fix | Delete
[3] Fix | Delete
use Automattic\WooCommerce\Blocks\Domain\Services\CheckoutFields;
[4] Fix | Delete
use Automattic\WooCommerce\Blocks\Package;
[5] Fix | Delete
use Automattic\WooCommerce\Internal\Customers\SearchService as CustomerSearchService;
[6] Fix | Delete
use Automattic\WooCommerce\StoreApi\Exceptions\RouteException;
[7] Fix | Delete
use Automattic\WooCommerce\Utilities\ArrayUtil;
[8] Fix | Delete
use Automattic\WooCommerce\Utilities\DiscountsUtil;
[9] Fix | Delete
use Automattic\WooCommerce\Utilities\ShippingUtil;
[10] Fix | Delete
use Exception;
[11] Fix | Delete
[12] Fix | Delete
/**
[13] Fix | Delete
* OrderController class.
[14] Fix | Delete
* Helper class which creates and syncs orders with the cart.
[15] Fix | Delete
*/
[16] Fix | Delete
class OrderController {
[17] Fix | Delete
[18] Fix | Delete
/**
[19] Fix | Delete
* Checkout fields controller.
[20] Fix | Delete
*
[21] Fix | Delete
* @var CheckoutFields
[22] Fix | Delete
*/
[23] Fix | Delete
private CheckoutFields $additional_fields_controller;
[24] Fix | Delete
[25] Fix | Delete
/**
[26] Fix | Delete
* Constructor.
[27] Fix | Delete
*/
[28] Fix | Delete
public function __construct() {
[29] Fix | Delete
$this->additional_fields_controller = Package::container()->get( CheckoutFields::class );
[30] Fix | Delete
}
[31] Fix | Delete
[32] Fix | Delete
/**
[33] Fix | Delete
* Create order and set props based on global settings.
[34] Fix | Delete
*
[35] Fix | Delete
* @throws RouteException Exception if invalid data is detected.
[36] Fix | Delete
*
[37] Fix | Delete
* @return \WC_Order A new order object.
[38] Fix | Delete
*/
[39] Fix | Delete
public function create_order_from_cart() {
[40] Fix | Delete
if ( wc()->cart->is_empty() ) {
[41] Fix | Delete
throw new RouteException(
[42] Fix | Delete
'woocommerce_rest_cart_empty',
[43] Fix | Delete
__( 'Cannot create order from empty cart.', 'woocommerce' ),
[44] Fix | Delete
400
[45] Fix | Delete
);
[46] Fix | Delete
}
[47] Fix | Delete
[48] Fix | Delete
add_filter( 'woocommerce_default_order_status', array( $this, 'default_order_status' ) );
[49] Fix | Delete
[50] Fix | Delete
$order = new \WC_Order();
[51] Fix | Delete
$order->set_status( 'checkout-draft' );
[52] Fix | Delete
$order->set_created_via( 'store-api' );
[53] Fix | Delete
$this->update_order_from_cart( $order );
[54] Fix | Delete
[55] Fix | Delete
remove_filter( 'woocommerce_default_order_status', array( $this, 'default_order_status' ) );
[56] Fix | Delete
[57] Fix | Delete
return $order;
[58] Fix | Delete
}
[59] Fix | Delete
[60] Fix | Delete
/**
[61] Fix | Delete
* Update an order using data from the current cart.
[62] Fix | Delete
*
[63] Fix | Delete
* @param \WC_Order $order The order object to update.
[64] Fix | Delete
* @param boolean $update_totals Whether to update totals or not.
[65] Fix | Delete
*/
[66] Fix | Delete
public function update_order_from_cart( \WC_Order $order, $update_totals = true ) {
[67] Fix | Delete
/**
[68] Fix | Delete
* This filter ensures that local pickup locations are still used for order taxes by forcing the address used to
[69] Fix | Delete
* calculate tax for an order to match the current address of the customer.
[70] Fix | Delete
*
[71] Fix | Delete
* - The method `$customer->get_taxable_address()` runs the filter `woocommerce_customer_taxable_address`.
[72] Fix | Delete
* - While we have a session, our `ShippingController::filter_taxable_address` function uses this hook to set
[73] Fix | Delete
* the customer address to the pickup location address if local pickup is the chosen method.
[74] Fix | Delete
*
[75] Fix | Delete
* Without this code in place, `$customer->get_taxable_address()` is not used when order taxes are calculated,
[76] Fix | Delete
* resulting in the wrong taxes being applied with local pickup.
[77] Fix | Delete
*
[78] Fix | Delete
* The alternative would be to instead use `woocommerce_order_get_tax_location` to return the pickup location
[79] Fix | Delete
* address directly, however since we have the customer filter in place we don't need to duplicate effort.
[80] Fix | Delete
*
[81] Fix | Delete
* @see \WC_Abstract_Order::get_tax_location()
[82] Fix | Delete
*/
[83] Fix | Delete
add_filter(
[84] Fix | Delete
'woocommerce_order_get_tax_location',
[85] Fix | Delete
function ( $location ) {
[86] Fix | Delete
[87] Fix | Delete
if ( ! is_null( wc()->customer ) ) {
[88] Fix | Delete
[89] Fix | Delete
$taxable_address = wc()->customer->get_taxable_address();
[90] Fix | Delete
[91] Fix | Delete
$location = array(
[92] Fix | Delete
'country' => $taxable_address[0],
[93] Fix | Delete
'state' => $taxable_address[1],
[94] Fix | Delete
'postcode' => $taxable_address[2],
[95] Fix | Delete
'city' => $taxable_address[3],
[96] Fix | Delete
);
[97] Fix | Delete
}
[98] Fix | Delete
[99] Fix | Delete
return $location;
[100] Fix | Delete
}
[101] Fix | Delete
);
[102] Fix | Delete
[103] Fix | Delete
// Ensure cart is current.
[104] Fix | Delete
if ( $update_totals ) {
[105] Fix | Delete
wc()->cart->calculate_totals();
[106] Fix | Delete
}
[107] Fix | Delete
[108] Fix | Delete
// Update the current order to match the current cart.
[109] Fix | Delete
$this->update_line_items_from_cart( $order );
[110] Fix | Delete
$this->update_addresses_from_cart( $order );
[111] Fix | Delete
$order->set_currency( get_woocommerce_currency() );
[112] Fix | Delete
$order->set_prices_include_tax( 'yes' === get_option( 'woocommerce_prices_include_tax' ) );
[113] Fix | Delete
$order->set_customer_id( get_current_user_id() );
[114] Fix | Delete
$order->set_customer_ip_address( \WC_Geolocation::get_ip_address() );
[115] Fix | Delete
$order->set_customer_user_agent( wc_get_user_agent() );
[116] Fix | Delete
$order->set_payment_method( PaymentUtils::get_default_payment_method() );
[117] Fix | Delete
$order->update_meta_data( 'is_vat_exempt', wc_bool_to_string( wc()->cart->get_customer()->get_is_vat_exempt() ) );
[118] Fix | Delete
$order->calculate_totals();
[119] Fix | Delete
}
[120] Fix | Delete
[121] Fix | Delete
/**
[122] Fix | Delete
* Copies order data to customer object (not the session), so values persist for future checkouts.
[123] Fix | Delete
*
[124] Fix | Delete
* @param \WC_Order $order Order object.
[125] Fix | Delete
*/
[126] Fix | Delete
public function sync_customer_data_with_order( \WC_Order $order ) {
[127] Fix | Delete
if ( $order->get_customer_id() ) {
[128] Fix | Delete
$customer = new \WC_Customer( $order->get_customer_id() );
[129] Fix | Delete
$customer->set_props(
[130] Fix | Delete
array(
[131] Fix | Delete
'billing_first_name' => $order->get_billing_first_name(),
[132] Fix | Delete
'billing_last_name' => $order->get_billing_last_name(),
[133] Fix | Delete
'billing_company' => $order->get_billing_company(),
[134] Fix | Delete
'billing_address_1' => $order->get_billing_address_1(),
[135] Fix | Delete
'billing_address_2' => $order->get_billing_address_2(),
[136] Fix | Delete
'billing_city' => $order->get_billing_city(),
[137] Fix | Delete
'billing_state' => $order->get_billing_state(),
[138] Fix | Delete
'billing_postcode' => $order->get_billing_postcode(),
[139] Fix | Delete
'billing_country' => $order->get_billing_country(),
[140] Fix | Delete
'billing_email' => $order->get_billing_email(),
[141] Fix | Delete
'billing_phone' => $order->get_billing_phone(),
[142] Fix | Delete
'shipping_first_name' => $order->get_shipping_first_name(),
[143] Fix | Delete
'shipping_last_name' => $order->get_shipping_last_name(),
[144] Fix | Delete
'shipping_company' => $order->get_shipping_company(),
[145] Fix | Delete
'shipping_address_1' => $order->get_shipping_address_1(),
[146] Fix | Delete
'shipping_address_2' => $order->get_shipping_address_2(),
[147] Fix | Delete
'shipping_city' => $order->get_shipping_city(),
[148] Fix | Delete
'shipping_state' => $order->get_shipping_state(),
[149] Fix | Delete
'shipping_postcode' => $order->get_shipping_postcode(),
[150] Fix | Delete
'shipping_country' => $order->get_shipping_country(),
[151] Fix | Delete
'shipping_phone' => $order->get_shipping_phone(),
[152] Fix | Delete
)
[153] Fix | Delete
);
[154] Fix | Delete
[155] Fix | Delete
$this->additional_fields_controller->sync_customer_additional_fields_with_order( $order, $customer );
[156] Fix | Delete
[157] Fix | Delete
$customer->save();
[158] Fix | Delete
}
[159] Fix | Delete
}
[160] Fix | Delete
[161] Fix | Delete
/**
[162] Fix | Delete
* Final validation ran before payment is taken.
[163] Fix | Delete
*
[164] Fix | Delete
* By this point we have an order populated with customer data and items.
[165] Fix | Delete
*
[166] Fix | Delete
* @throws RouteException Exception if invalid data is detected.
[167] Fix | Delete
* @param \WC_Order $order Order object.
[168] Fix | Delete
*/
[169] Fix | Delete
public function validate_order_before_payment( \WC_Order $order ) {
[170] Fix | Delete
$needs_shipping = wc()->cart->needs_shipping();
[171] Fix | Delete
$chosen_shipping_methods = wc()->session->get( 'chosen_shipping_methods', [] );
[172] Fix | Delete
[173] Fix | Delete
$this->validate_coupons( $order );
[174] Fix | Delete
$this->validate_email( $order );
[175] Fix | Delete
$this->validate_selected_shipping_methods( $needs_shipping, $chosen_shipping_methods );
[176] Fix | Delete
$this->validate_addresses( $order, $needs_shipping );
[177] Fix | Delete
[178] Fix | Delete
// Perform custom validations.
[179] Fix | Delete
$this->perform_custom_order_validation( $order );
[180] Fix | Delete
}
[181] Fix | Delete
[182] Fix | Delete
/**
[183] Fix | Delete
* Final validation for existing orders, ran before payment is taken.
[184] Fix | Delete
*
[185] Fix | Delete
* By this point we have an order populated with customer data and items.
[186] Fix | Delete
*
[187] Fix | Delete
* Since the cart is not involved, we don't validate shipping methods and assume the order already
[188] Fix | Delete
* contains the correct shipping items.
[189] Fix | Delete
*
[190] Fix | Delete
* @throws RouteException Exception if invalid data is detected.
[191] Fix | Delete
* @param \WC_Order $order Order object.
[192] Fix | Delete
*/
[193] Fix | Delete
public function validate_existing_order_before_payment( \WC_Order $order ) {
[194] Fix | Delete
$needs_shipping = $order->needs_shipping();
[195] Fix | Delete
[196] Fix | Delete
$this->validate_coupons( $order, true );
[197] Fix | Delete
$this->validate_email( $order );
[198] Fix | Delete
$this->validate_addresses( $order, $needs_shipping );
[199] Fix | Delete
[200] Fix | Delete
// Perform custom validations.
[201] Fix | Delete
$this->perform_custom_order_validation( $order );
[202] Fix | Delete
}
[203] Fix | Delete
[204] Fix | Delete
/**
[205] Fix | Delete
* Perform custom order validation via WooCommerce hooks.
[206] Fix | Delete
*
[207] Fix | Delete
* Allows plugins to perform custom validation before payment.
[208] Fix | Delete
*
[209] Fix | Delete
* @param \WC_Order $order Order object.
[210] Fix | Delete
* @throws RouteException Exception if validation fails.
[211] Fix | Delete
*/
[212] Fix | Delete
protected function perform_custom_order_validation( \WC_Order $order ) {
[213] Fix | Delete
$validation_errors = new \WP_Error();
[214] Fix | Delete
[215] Fix | Delete
/**
[216] Fix | Delete
* Allow plugins to perform custom validation before payment.
[217] Fix | Delete
*
[218] Fix | Delete
* Plugins can add errors to the $validation_errors object.
[219] Fix | Delete
*
[220] Fix | Delete
* @param \WC_Order $order The order object.
[221] Fix | Delete
* @param \WP_Error $validation_errors WP_Error object to add custom errors to.
[222] Fix | Delete
* @since 9.9.0
[223] Fix | Delete
*/
[224] Fix | Delete
do_action( 'woocommerce_checkout_validate_order_before_payment', $order, $validation_errors );
[225] Fix | Delete
[226] Fix | Delete
// Check if there are any errors after custom validation.
[227] Fix | Delete
if ( $validation_errors->has_errors() ) {
[228] Fix | Delete
throw new RouteException(
[229] Fix | Delete
'woocommerce_rest_checkout_custom_validation_error',
[230] Fix | Delete
esc_html( implode( ' ', $validation_errors->get_error_messages() ) ),
[231] Fix | Delete
400
[232] Fix | Delete
);
[233] Fix | Delete
}
[234] Fix | Delete
}
[235] Fix | Delete
[236] Fix | Delete
/**
[237] Fix | Delete
* Convert a coupon code to a coupon object.
[238] Fix | Delete
*
[239] Fix | Delete
* @param string $coupon_code Coupon code.
[240] Fix | Delete
* @return \WC_Coupon Coupon object.
[241] Fix | Delete
*/
[242] Fix | Delete
protected function get_coupon( $coupon_code ) {
[243] Fix | Delete
return new \WC_Coupon( $coupon_code );
[244] Fix | Delete
}
[245] Fix | Delete
[246] Fix | Delete
/**
[247] Fix | Delete
* Validate coupons applied to the order and remove those that are not valid.
[248] Fix | Delete
*
[249] Fix | Delete
* @throws RouteException Exception if invalid data is detected.
[250] Fix | Delete
* @param \WC_Order $order Order object.
[251] Fix | Delete
* @param bool $use_order_data Whether to use order data or cart data.
[252] Fix | Delete
*/
[253] Fix | Delete
protected function validate_coupons( \WC_Order $order, bool $use_order_data = false ) {
[254] Fix | Delete
$coupon_codes = $order->get_coupon_codes();
[255] Fix | Delete
$coupons = array_filter( array_map( array( $this, 'get_coupon' ), $coupon_codes ) );
[256] Fix | Delete
$validators = array( 'validate_coupon_email_restriction', 'validate_coupon_usage_limit' );
[257] Fix | Delete
$coupon_errors = array();
[258] Fix | Delete
[259] Fix | Delete
foreach ( $coupons as $coupon ) {
[260] Fix | Delete
try {
[261] Fix | Delete
array_walk(
[262] Fix | Delete
$validators,
[263] Fix | Delete
function ( $validator, $index, $params ) {
[264] Fix | Delete
call_user_func_array( array( $this, $validator ), $params );
[265] Fix | Delete
},
[266] Fix | Delete
array( $coupon, $order )
[267] Fix | Delete
);
[268] Fix | Delete
} catch ( Exception $error ) {
[269] Fix | Delete
$coupon_errors[ $coupon->get_code() ] = $error->getMessage();
[270] Fix | Delete
}
[271] Fix | Delete
}
[272] Fix | Delete
[273] Fix | Delete
if ( $coupon_errors ) {
[274] Fix | Delete
// Remove all coupons that were not valid.
[275] Fix | Delete
if ( $use_order_data ) {
[276] Fix | Delete
$error_code = 'woocommerce_rest_order_coupon_errors';
[277] Fix | Delete
[278] Fix | Delete
foreach ( $coupon_errors as $coupon_code => $message ) {
[279] Fix | Delete
$order->remove_coupon( $coupon_code );
[280] Fix | Delete
}
[281] Fix | Delete
[282] Fix | Delete
// Recalculate totals.
[283] Fix | Delete
$order->calculate_totals();
[284] Fix | Delete
} else {
[285] Fix | Delete
$error_code = 'woocommerce_rest_cart_coupon_errors';
[286] Fix | Delete
[287] Fix | Delete
foreach ( $coupon_errors as $coupon_code => $message ) {
[288] Fix | Delete
wc()->cart->remove_coupon( $coupon_code );
[289] Fix | Delete
}
[290] Fix | Delete
[291] Fix | Delete
// Recalculate totals.
[292] Fix | Delete
wc()->cart->calculate_totals();
[293] Fix | Delete
[294] Fix | Delete
// Re-sync order with cart.
[295] Fix | Delete
$this->update_order_from_cart( $order );
[296] Fix | Delete
}
[297] Fix | Delete
[298] Fix | Delete
// Return exception so customer can review before payment.
[299] Fix | Delete
if ( 1 === count( $coupon_errors ) && $use_order_data ) {
[300] Fix | Delete
$error_message = sprintf(
[301] Fix | Delete
/* translators: %1$s Coupon codes, %2$s Reason */
[302] Fix | Delete
__( '"%1$s" was removed from the order. %2$s', 'woocommerce' ),
[303] Fix | Delete
array_keys( $coupon_errors )[0],
[304] Fix | Delete
array_values( $coupon_errors )[0],
[305] Fix | Delete
);
[306] Fix | Delete
} elseif ( 1 === count( $coupon_errors ) ) {
[307] Fix | Delete
$error_message = sprintf(
[308] Fix | Delete
/* translators: %1$s Coupon codes, %2$s Reason */
[309] Fix | Delete
__( '"%1$s" was removed from the cart. %2$s', 'woocommerce' ),
[310] Fix | Delete
array_keys( $coupon_errors )[0],
[311] Fix | Delete
array_values( $coupon_errors )[0],
[312] Fix | Delete
);
[313] Fix | Delete
} elseif ( $use_order_data ) {
[314] Fix | Delete
$error_message = sprintf(
[315] Fix | Delete
/* translators: %s Coupon codes. */
[316] Fix | Delete
__( 'Invalid coupons were removed from the order: "%s"', 'woocommerce' ),
[317] Fix | Delete
implode( '", "', array_keys( $coupon_errors ) )
[318] Fix | Delete
);
[319] Fix | Delete
} else {
[320] Fix | Delete
$error_message = sprintf(
[321] Fix | Delete
/* translators: %s Coupon codes. */
[322] Fix | Delete
__( 'Invalid coupons were removed from the cart: "%s"', 'woocommerce' ),
[323] Fix | Delete
implode( '", "', array_keys( $coupon_errors ) )
[324] Fix | Delete
);
[325] Fix | Delete
}
[326] Fix | Delete
[327] Fix | Delete
throw new RouteException( $error_code, $error_message, 409, array( 'removed_coupons' => $coupon_errors ) ); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
[328] Fix | Delete
}
[329] Fix | Delete
}
[330] Fix | Delete
[331] Fix | Delete
/**
[332] Fix | Delete
* Validates the customer email. This is a required field.
[333] Fix | Delete
*
[334] Fix | Delete
* @throws RouteException Exception if invalid data is detected.
[335] Fix | Delete
* @param \WC_Order $order Order object.
[336] Fix | Delete
*/
[337] Fix | Delete
protected function validate_email( \WC_Order $order ) {
[338] Fix | Delete
$email = $order->get_billing_email();
[339] Fix | Delete
[340] Fix | Delete
if ( empty( $email ) ) {
[341] Fix | Delete
throw new RouteException(
[342] Fix | Delete
'woocommerce_rest_missing_email_address',
[343] Fix | Delete
__( 'A valid email address is required', 'woocommerce' ),
[344] Fix | Delete
400
[345] Fix | Delete
);
[346] Fix | Delete
}
[347] Fix | Delete
[348] Fix | Delete
if ( ! is_email( $email ) ) {
[349] Fix | Delete
throw new RouteException(
[350] Fix | Delete
'woocommerce_rest_invalid_email_address',
[351] Fix | Delete
sprintf(
[352] Fix | Delete
/* translators: %s provided email. */
[353] Fix | Delete
__( 'The provided email address (%s) is not valid—please provide a valid email address', 'woocommerce' ),
[354] Fix | Delete
esc_html( $email )
[355] Fix | Delete
),
[356] Fix | Delete
400
[357] Fix | Delete
);
[358] Fix | Delete
}
[359] Fix | Delete
}
[360] Fix | Delete
[361] Fix | Delete
/**
[362] Fix | Delete
* Validates customer address data based on the locale to ensure required fields are set.
[363] Fix | Delete
*
[364] Fix | Delete
* @throws RouteException Exception if invalid data is detected.
[365] Fix | Delete
* @param \WC_Order $order Order object.
[366] Fix | Delete
* @param bool $needs_shipping Whether the order needs shipping.
[367] Fix | Delete
*/
[368] Fix | Delete
protected function validate_addresses( \WC_Order $order, bool $needs_shipping ) {
[369] Fix | Delete
$errors = new \WP_Error();
[370] Fix | Delete
$billing_country = $order->get_billing_country();
[371] Fix | Delete
$shipping_country = $order->get_shipping_country();
[372] Fix | Delete
[373] Fix | Delete
if ( $needs_shipping ) {
[374] Fix | Delete
$local_pickup_method_ids = LocalPickupUtils::get_local_pickup_method_ids();
[375] Fix | Delete
$selected_shipping_rates = ShippingUtil::get_selected_shipping_rates_from_packages( WC()->shipping()->get_packages() );
[376] Fix | Delete
$selected_shipping_rates_are_all_local_pickup = ArrayUtil::array_all(
[377] Fix | Delete
$selected_shipping_rates,
[378] Fix | Delete
function ( $rate ) use ( $local_pickup_method_ids ) {
[379] Fix | Delete
return in_array( $rate->get_method_id(), $local_pickup_method_ids, true );
[380] Fix | Delete
}
[381] Fix | Delete
);
[382] Fix | Delete
[383] Fix | Delete
// If only local pickup is selected, we don't need to validate the shipping country.
[384] Fix | Delete
if ( ! $selected_shipping_rates_are_all_local_pickup && ! $this->validate_allowed_country( $shipping_country, (array) wc()->countries->get_shipping_countries() ) ) {
[385] Fix | Delete
throw new RouteException(
[386] Fix | Delete
'woocommerce_rest_invalid_address_country',
[387] Fix | Delete
sprintf(
[388] Fix | Delete
/* translators: %s country code. */
[389] Fix | Delete
esc_html__( 'Sorry, we do not ship orders to the provided country (%s)', 'woocommerce' ),
[390] Fix | Delete
esc_html( $shipping_country )
[391] Fix | Delete
),
[392] Fix | Delete
400,
[393] Fix | Delete
array(
[394] Fix | Delete
'allowed_countries' => array_map( 'esc_html', array_keys( wc()->countries->get_shipping_countries() ) ),
[395] Fix | Delete
)
[396] Fix | Delete
);
[397] Fix | Delete
}
[398] Fix | Delete
}
[399] Fix | Delete
[400] Fix | Delete
if ( ! $this->validate_allowed_country( $billing_country, (array) wc()->countries->get_allowed_countries() ) ) {
[401] Fix | Delete
throw new RouteException(
[402] Fix | Delete
'woocommerce_rest_invalid_address_country',
[403] Fix | Delete
sprintf(
[404] Fix | Delete
/* translators: %s country code. */
[405] Fix | Delete
esc_html__( 'Sorry, we do not allow orders from the provided country (%s)', 'woocommerce' ),
[406] Fix | Delete
esc_html( $billing_country )
[407] Fix | Delete
),
[408] Fix | Delete
400,
[409] Fix | Delete
array(
[410] Fix | Delete
'allowed_countries' => array_map( 'esc_html', array_keys( wc()->countries->get_allowed_countries() ) ),
[411] Fix | Delete
)
[412] Fix | Delete
);
[413] Fix | Delete
}
[414] Fix | Delete
[415] Fix | Delete
if ( $needs_shipping ) {
[416] Fix | Delete
$this->validate_address_fields( $order, 'shipping', $errors );
[417] Fix | Delete
}
[418] Fix | Delete
$this->validate_address_fields( $order, 'billing', $errors );
[419] Fix | Delete
[420] Fix | Delete
if ( ! $errors->has_errors() ) {
[421] Fix | Delete
return;
[422] Fix | Delete
}
[423] Fix | Delete
[424] Fix | Delete
$errors_by_code = array();
[425] Fix | Delete
$error_codes = $errors->get_error_codes();
[426] Fix | Delete
[427] Fix | Delete
foreach ( $error_codes as $code ) {
[428] Fix | Delete
$errors_by_code[ $code ] = $errors->get_error_messages( $code );
[429] Fix | Delete
}
[430] Fix | Delete
[431] Fix | Delete
// Surface errors from first code.
[432] Fix | Delete
foreach ( $errors_by_code as $code => $error_messages ) {
[433] Fix | Delete
throw new RouteException(
[434] Fix | Delete
'woocommerce_rest_invalid_address',
[435] Fix | Delete
sprintf(
[436] Fix | Delete
/* translators: %s Address type. */
[437] Fix | Delete
esc_html__( 'There was a problem with the provided %s:', 'woocommerce' ) . ' ' . esc_html( implode( ', ', $error_messages ) ),
[438] Fix | Delete
'shipping' === $code ? esc_html__( 'shipping address', 'woocommerce' ) : esc_html__( 'billing address', 'woocommerce' )
[439] Fix | Delete
),
[440] Fix | Delete
400,
[441] Fix | Delete
array(
[442] Fix | Delete
'errors' => $errors_by_code, // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
[443] Fix | Delete
)
[444] Fix | Delete
);
[445] Fix | Delete
}
[446] Fix | Delete
}
[447] Fix | Delete
[448] Fix | Delete
/**
[449] Fix | Delete
* Check all required address fields are set and return errors if not.
[450] Fix | Delete
*
[451] Fix | Delete
* @param string $country Country code.
[452] Fix | Delete
* @param array $allowed_countries List of valid country codes.
[453] Fix | Delete
* @return boolean True if valid.
[454] Fix | Delete
*/
[455] Fix | Delete
protected function validate_allowed_country( $country, array $allowed_countries ) {
[456] Fix | Delete
return array_key_exists( $country, $allowed_countries );
[457] Fix | Delete
}
[458] Fix | Delete
[459] Fix | Delete
/**
[460] Fix | Delete
* Check all required address fields are set and return errors if not.
[461] Fix | Delete
*
[462] Fix | Delete
* @param \WC_Order $order Order object.
[463] Fix | Delete
* @param string $address_type billing or shipping address, used in error messages.
[464] Fix | Delete
* @param \WP_Error $errors Error object.
[465] Fix | Delete
*/
[466] Fix | Delete
protected function validate_address_fields( \WC_Order $order, $address_type, \WP_Error $errors ) {
[467] Fix | Delete
$all_locales = wc()->countries->get_country_locale();
[468] Fix | Delete
$address = $order->get_address( $address_type );
[469] Fix | Delete
$current_locale = $all_locales[ $address['country'] ] ?? [];
[470] Fix | Delete
[471] Fix | Delete
foreach ( $all_locales['default'] as $key => $value ) {
[472] Fix | Delete
// If $current_locale[ $key ] is not empty, merge it with locale default, otherwise just use default locale.
[473] Fix | Delete
$current_locale[ $key ] = ! empty( $current_locale[ $key ] )
[474] Fix | Delete
? wp_parse_args( $current_locale[ $key ], $value )
[475] Fix | Delete
: $value;
[476] Fix | Delete
}
[477] Fix | Delete
[478] Fix | Delete
$additional_fields = $this->additional_fields_controller->get_all_fields_from_object( $order, $address_type );
[479] Fix | Delete
[480] Fix | Delete
$address = array_merge( $address, $additional_fields );
[481] Fix | Delete
[482] Fix | Delete
foreach ( $current_locale as $address_field_key => $address_field ) {
[483] Fix | Delete
// Skip validation if field is not required or if it is hidden.
[484] Fix | Delete
if (
[485] Fix | Delete
true !== wc_string_to_bool( $address_field['required'] ?? false ) ||
[486] Fix | Delete
true === wc_string_to_bool( $address_field['hidden'] ?? false )
[487] Fix | Delete
) {
[488] Fix | Delete
continue;
[489] Fix | Delete
}
[490] Fix | Delete
[491] Fix | Delete
// Check if field is not set, is an empty string, or is an empty array.
[492] Fix | Delete
$is_empty = ! isset( $address[ $address_field_key ] ) ||
[493] Fix | Delete
( is_string( $address[ $address_field_key ] ) && '' === trim( $address[ $address_field_key ] ) ) ||
[494] Fix | Delete
( is_array( $address[ $address_field_key ] ) && 0 === count( $address[ $address_field_key ] ) );
[495] Fix | Delete
[496] Fix | Delete
if ( $is_empty ) {
[497] Fix | Delete
/* translators: %s Field label. */
[498] Fix | Delete
$errors->add( $address_type, sprintf( __( '%s is required', 'woocommerce' ), $address_field['label'] ), $address_field_key );
[499] Fix | Delete
12
It is recommended that you Edit text format, this type of Fix handles quite a lot in one request
Function