Edit File by line
/home/zeestwma/richards.../wp-conte.../plugins/woocomme.../src/StoreApi/Utilitie...
File: OrderController.php
}
[500] Fix | Delete
}
[501] Fix | Delete
[502] Fix | Delete
// Validate additional fields.
[503] Fix | Delete
$result = $this->additional_fields_controller->validate_fields_for_location( $address, 'address', $address_type );
[504] Fix | Delete
[505] Fix | Delete
if ( $result->has_errors() ) {
[506] Fix | Delete
// Add errors to main error object but ensure they maintain the billing/shipping error code.
[507] Fix | Delete
foreach ( $result->get_error_codes() as $code ) {
[508] Fix | Delete
$errors->add( $address_type, $result->get_error_message( $code ), $code );
[509] Fix | Delete
}
[510] Fix | Delete
}
[511] Fix | Delete
}
[512] Fix | Delete
[513] Fix | Delete
/**
[514] Fix | Delete
* Check email restrictions of a coupon against the order.
[515] Fix | Delete
*
[516] Fix | Delete
* @throws Exception Exception if invalid data is detected.
[517] Fix | Delete
* @param \WC_Coupon $coupon Coupon object applied to the cart.
[518] Fix | Delete
* @param \WC_Order $order Order object.
[519] Fix | Delete
*/
[520] Fix | Delete
protected function validate_coupon_email_restriction( \WC_Coupon $coupon, \WC_Order $order ) {
[521] Fix | Delete
$restrictions = $coupon->get_email_restrictions();
[522] Fix | Delete
[523] Fix | Delete
if ( empty( $restrictions ) ) {
[524] Fix | Delete
return;
[525] Fix | Delete
}
[526] Fix | Delete
[527] Fix | Delete
$check_emails = array();
[528] Fix | Delete
[529] Fix | Delete
// Check the logged-in user's email.
[530] Fix | Delete
$current_user = wp_get_current_user();
[531] Fix | Delete
if ( $current_user->exists() ) {
[532] Fix | Delete
$user_email = trim( sanitize_email( $current_user->user_email ) );
[533] Fix | Delete
if ( ! empty( $user_email ) ) {
[534] Fix | Delete
$check_emails[] = strtolower( $user_email );
[535] Fix | Delete
}
[536] Fix | Delete
}
[537] Fix | Delete
[538] Fix | Delete
// Also check the billing email from the order.
[539] Fix | Delete
$billing_email = $order->get_billing_email();
[540] Fix | Delete
if ( ! empty( $billing_email ) ) {
[541] Fix | Delete
$billing_email = trim( sanitize_email( $billing_email ) );
[542] Fix | Delete
if ( ! empty( $billing_email ) ) {
[543] Fix | Delete
$check_emails[] = strtolower( $billing_email );
[544] Fix | Delete
}
[545] Fix | Delete
}
[546] Fix | Delete
[547] Fix | Delete
// Remove duplicates and empty values.
[548] Fix | Delete
$check_emails = array_unique( array_filter( $check_emails ) );
[549] Fix | Delete
[550] Fix | Delete
if ( ! empty( $check_emails ) && ! DiscountsUtil::is_coupon_emails_allowed( $check_emails, $restrictions ) ) {
[551] Fix | Delete
// phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
[552] Fix | Delete
throw new Exception( $coupon->get_coupon_error( \WC_Coupon::E_WC_COUPON_NOT_YOURS_REMOVED ) );
[553] Fix | Delete
}
[554] Fix | Delete
}
[555] Fix | Delete
[556] Fix | Delete
/**
[557] Fix | Delete
* Check usage restrictions of a coupon against the order.
[558] Fix | Delete
*
[559] Fix | Delete
* @throws Exception Exception if invalid data is detected.
[560] Fix | Delete
* @param \WC_Coupon $coupon Coupon object applied to the cart.
[561] Fix | Delete
* @param \WC_Order $order Order object.
[562] Fix | Delete
*/
[563] Fix | Delete
protected function validate_coupon_usage_limit( \WC_Coupon $coupon, \WC_Order $order ) {
[564] Fix | Delete
$coupon_usage_limit = $coupon->get_usage_limit_per_user();
[565] Fix | Delete
[566] Fix | Delete
if ( 0 === $coupon_usage_limit ) {
[567] Fix | Delete
return;
[568] Fix | Delete
}
[569] Fix | Delete
[570] Fix | Delete
// First, we check a logged in customer usage count, which happens against their user id, billing email, and account email.
[571] Fix | Delete
if ( $order->get_customer_id() ) {
[572] Fix | Delete
// We get usage per user id and associated emails.
[573] Fix | Delete
$usage_count = $this->get_usage_per_aliases(
[574] Fix | Delete
$coupon,
[575] Fix | Delete
array(
[576] Fix | Delete
$order->get_billing_email(),
[577] Fix | Delete
$order->get_customer_id(),
[578] Fix | Delete
$this->get_email_from_user_id( $order->get_customer_id() ),
[579] Fix | Delete
)
[580] Fix | Delete
);
[581] Fix | Delete
} else {
[582] Fix | Delete
// Otherwise we check if the email doesn't belong to an existing user.
[583] Fix | Delete
// This will get us any user ids for the given billing email.
[584] Fix | Delete
$user_ids = wc_get_container()->get( CustomerSearchService::class )->find_user_ids_by_billing_email_for_coupons_usage_lookup( array( $order->get_billing_email() ) );
[585] Fix | Delete
[586] Fix | Delete
// Convert all found user ids to a list of email addresses.
[587] Fix | Delete
$user_emails = array_map( array( $this, 'get_email_from_user_id' ), $user_ids );
[588] Fix | Delete
[589] Fix | Delete
// This matches a user against the given billing email and gets their ID/email/billing email.
[590] Fix | Delete
$found_user = get_user_by( 'email', $order->get_billing_email() );
[591] Fix | Delete
if ( $found_user ) {
[592] Fix | Delete
$user_ids[] = $found_user->ID;
[593] Fix | Delete
$user_emails[] = $found_user->user_email;
[594] Fix | Delete
$user_emails[] = get_user_meta( $found_user->ID, 'billing_email', true );
[595] Fix | Delete
}
[596] Fix | Delete
[597] Fix | Delete
// Finally, grab usage count for all found IDs and emails.
[598] Fix | Delete
$usage_count = $this->get_usage_per_aliases(
[599] Fix | Delete
$coupon,
[600] Fix | Delete
array_merge(
[601] Fix | Delete
$user_emails,
[602] Fix | Delete
$user_ids,
[603] Fix | Delete
array( $order->get_billing_email() )
[604] Fix | Delete
)
[605] Fix | Delete
);
[606] Fix | Delete
}
[607] Fix | Delete
[608] Fix | Delete
if ( $usage_count >= $coupon_usage_limit ) {
[609] Fix | Delete
throw new Exception( $coupon->get_coupon_error( \WC_Coupon::E_WC_COUPON_USAGE_LIMIT_REACHED ) );
[610] Fix | Delete
}
[611] Fix | Delete
}
[612] Fix | Delete
[613] Fix | Delete
/**
[614] Fix | Delete
* Get user email from user id.
[615] Fix | Delete
*
[616] Fix | Delete
* @param integer $user_id User ID.
[617] Fix | Delete
* @return string Email or empty string.
[618] Fix | Delete
*/
[619] Fix | Delete
private function get_email_from_user_id( $user_id ) {
[620] Fix | Delete
$user_data = get_userdata( $user_id );
[621] Fix | Delete
return $user_data ? $user_data->user_email : '';
[622] Fix | Delete
}
[623] Fix | Delete
[624] Fix | Delete
/**
[625] Fix | Delete
* Get the usage count for a coupon based on a list of aliases (ids, emails).
[626] Fix | Delete
*
[627] Fix | Delete
* @param \WC_Coupon $coupon Coupon object applied to the cart.
[628] Fix | Delete
* @param array $aliases List of aliases to check.
[629] Fix | Delete
*
[630] Fix | Delete
* @return integer
[631] Fix | Delete
*/
[632] Fix | Delete
private function get_usage_per_aliases( $coupon, $aliases ) {
[633] Fix | Delete
global $wpdb;
[634] Fix | Delete
$aliases = array_unique( array_filter( $aliases ) );
[635] Fix | Delete
$aliases_string = "('" . implode( "','", array_map( 'esc_sql', $aliases ) ) . "')";
[636] Fix | Delete
$usage_count = $wpdb->get_var(
[637] Fix | Delete
$wpdb->prepare(
[638] Fix | Delete
// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
[639] Fix | Delete
"SELECT COUNT( meta_id ) FROM {$wpdb->postmeta} WHERE post_id = %d AND meta_key = '_used_by' AND meta_value IN {$aliases_string};",
[640] Fix | Delete
$coupon->get_id(),
[641] Fix | Delete
)
[642] Fix | Delete
);
[643] Fix | Delete
[644] Fix | Delete
$data_store = $coupon->get_data_store();
[645] Fix | Delete
// Coupons can be held for an x amount of time before being applied to an order, so we need to check if it's already being held in (maybe via another flow).
[646] Fix | Delete
$tentative_usage_count = $data_store->get_tentative_usages_for_user( $coupon->get_id(), $aliases );
[647] Fix | Delete
return $tentative_usage_count + $usage_count;
[648] Fix | Delete
}
[649] Fix | Delete
[650] Fix | Delete
/**
[651] Fix | Delete
* Check there is a shipping method if it requires shipping.
[652] Fix | Delete
*
[653] Fix | Delete
* @throws RouteException Exception if invalid data is detected.
[654] Fix | Delete
* @param boolean $needs_shipping Current order needs shipping.
[655] Fix | Delete
* @param array $chosen_shipping_methods Array of shipping methods.
[656] Fix | Delete
*/
[657] Fix | Delete
public function validate_selected_shipping_methods( $needs_shipping, $chosen_shipping_methods = array() ) {
[658] Fix | Delete
if ( ! $needs_shipping ) {
[659] Fix | Delete
return;
[660] Fix | Delete
}
[661] Fix | Delete
[662] Fix | Delete
$exception = new RouteException(
[663] Fix | Delete
'woocommerce_rest_invalid_shipping_option',
[664] Fix | Delete
__( 'Sorry, this order requires a shipping option.', 'woocommerce' ),
[665] Fix | Delete
400,
[666] Fix | Delete
array()
[667] Fix | Delete
);
[668] Fix | Delete
[669] Fix | Delete
if ( ! is_array( $chosen_shipping_methods ) || empty( $chosen_shipping_methods ) ) {
[670] Fix | Delete
throw $exception;
[671] Fix | Delete
}
[672] Fix | Delete
[673] Fix | Delete
// Validate that the chosen shipping methods are valid according to the returned package rates.
[674] Fix | Delete
$packages = WC()->shipping()->get_packages();
[675] Fix | Delete
foreach ( $packages as $package_id => $package ) {
[676] Fix | Delete
$chosen_rate_for_package = $chosen_shipping_methods[ $package_id ];
[677] Fix | Delete
$valid_rate_ids_for_package = wp_list_pluck( $package['rates'], 'id' );
[678] Fix | Delete
[679] Fix | Delete
if ( ! is_string( $chosen_rate_for_package ) || ! ArrayUtils::string_contains_array( $chosen_rate_for_package, $valid_rate_ids_for_package ) ) {
[680] Fix | Delete
throw $exception;
[681] Fix | Delete
}
[682] Fix | Delete
}
[683] Fix | Delete
}
[684] Fix | Delete
[685] Fix | Delete
/**
[686] Fix | Delete
* Validate a given order key against an existing order.
[687] Fix | Delete
*
[688] Fix | Delete
* @throws RouteException Exception if invalid data is detected.
[689] Fix | Delete
* @param integer $order_id Order ID.
[690] Fix | Delete
* @param string $order_key Order key.
[691] Fix | Delete
*/
[692] Fix | Delete
public function validate_order_key( $order_id, $order_key ) {
[693] Fix | Delete
$order = wc_get_order( $order_id );
[694] Fix | Delete
[695] Fix | Delete
if ( ! $order || ! $order_key || $order->get_id() !== $order_id || ! hash_equals( $order->get_order_key(), $order_key ) ) {
[696] Fix | Delete
throw new RouteException( 'woocommerce_rest_invalid_order', __( 'Invalid order ID or key provided.', 'woocommerce' ), 401 );
[697] Fix | Delete
}
[698] Fix | Delete
}
[699] Fix | Delete
[700] Fix | Delete
/**
[701] Fix | Delete
* Get errors for order stock on failed orders.
[702] Fix | Delete
*
[703] Fix | Delete
* @throws RouteException Exception if invalid data is detected.
[704] Fix | Delete
* @param integer $order_id Order ID.
[705] Fix | Delete
*/
[706] Fix | Delete
public function get_failed_order_stock_error( $order_id ) {
[707] Fix | Delete
$order = wc_get_order( $order_id );
[708] Fix | Delete
[709] Fix | Delete
// Ensure order items are still stocked if paying for a failed order. Pending orders do not need this check because stock is held.
[710] Fix | Delete
if ( ! $order->has_status( wc_get_is_pending_statuses() ) ) {
[711] Fix | Delete
$quantities = array();
[712] Fix | Delete
[713] Fix | Delete
foreach ( $order->get_items() as $item_key => $item ) {
[714] Fix | Delete
if ( $item && is_callable( array( $item, 'get_product' ) ) ) {
[715] Fix | Delete
$product = $item->get_product();
[716] Fix | Delete
[717] Fix | Delete
if ( ! $product ) {
[718] Fix | Delete
continue;
[719] Fix | Delete
}
[720] Fix | Delete
[721] Fix | Delete
$quantities[ $product->get_stock_managed_by_id() ] = isset( $quantities[ $product->get_stock_managed_by_id() ] ) ? $quantities[ $product->get_stock_managed_by_id() ] + $item->get_quantity() : $item->get_quantity();
[722] Fix | Delete
}
[723] Fix | Delete
}
[724] Fix | Delete
[725] Fix | Delete
// Stock levels may already have been adjusted for this order (in which case we don't need to worry about checking for low stock).
[726] Fix | Delete
if ( ! $order->get_data_store()->get_stock_reduced( $order->get_id() ) ) {
[727] Fix | Delete
foreach ( $order->get_items() as $item_key => $item ) {
[728] Fix | Delete
if ( $item && is_callable( array( $item, 'get_product' ) ) ) {
[729] Fix | Delete
$product = $item->get_product();
[730] Fix | Delete
[731] Fix | Delete
if ( ! $product ) {
[732] Fix | Delete
continue;
[733] Fix | Delete
}
[734] Fix | Delete
[735] Fix | Delete
/**
[736] Fix | Delete
* Filters whether or not the product is in stock for this pay for order.
[737] Fix | Delete
*
[738] Fix | Delete
* @param boolean True if in stock.
[739] Fix | Delete
* @param \WC_Product $product Product.
[740] Fix | Delete
* @param \WC_Order $order Order.
[741] Fix | Delete
*
[742] Fix | Delete
* @since 9.8.0-dev
[743] Fix | Delete
*/
[744] Fix | Delete
if ( ! apply_filters( 'woocommerce_pay_order_product_in_stock', $product->is_in_stock(), $product, $order ) ) {
[745] Fix | Delete
return array(
[746] Fix | Delete
'code' => 'woocommerce_rest_out_of_stock',
[747] Fix | Delete
/* translators: %s: product name */
[748] Fix | Delete
'message' => sprintf( __( 'Sorry, "%s" is no longer in stock so this order cannot be paid for. We apologize for any inconvenience caused.', 'woocommerce' ), $product->get_name() ),
[749] Fix | Delete
);
[750] Fix | Delete
}
[751] Fix | Delete
[752] Fix | Delete
// We only need to check products managing stock, with a limited stock qty.
[753] Fix | Delete
if ( ! $product->managing_stock() || $product->backorders_allowed() ) {
[754] Fix | Delete
continue;
[755] Fix | Delete
}
[756] Fix | Delete
[757] Fix | Delete
// Check stock based on all items in the cart and consider any held stock within pending orders.
[758] Fix | Delete
$held_stock = wc_get_held_stock_quantity( $product, $order->get_id() );
[759] Fix | Delete
$required_stock = $quantities[ $product->get_stock_managed_by_id() ];
[760] Fix | Delete
[761] Fix | Delete
/**
[762] Fix | Delete
* Filters whether or not the product has enough stock.
[763] Fix | Delete
*
[764] Fix | Delete
* @param boolean True if has enough stock.
[765] Fix | Delete
* @param \WC_Product $product Product.
[766] Fix | Delete
* @param \WC_Order $order Order.
[767] Fix | Delete
*
[768] Fix | Delete
* @since 9.8.0-dev
[769] Fix | Delete
*/
[770] Fix | Delete
if ( ! apply_filters( 'woocommerce_pay_order_product_has_enough_stock', ( $product->get_stock_quantity() >= ( $held_stock + $required_stock ) ), $product, $order ) ) {
[771] Fix | Delete
/* translators: 1: product name 2: quantity in stock */
[772] Fix | Delete
return array(
[773] Fix | Delete
'code' => 'woocommerce_rest_out_of_stock',
[774] Fix | Delete
/* translators: %s: product name */
[775] Fix | Delete
'message' => sprintf( __( 'Sorry, we do not have enough "%1$s" in stock to fulfill your order (%2$s available). We apologize for any inconvenience caused.', 'woocommerce' ), $product->get_name(), wc_format_stock_quantity_for_display( $product->get_stock_quantity() - $held_stock, $product ) ),
[776] Fix | Delete
);
[777] Fix | Delete
}
[778] Fix | Delete
}
[779] Fix | Delete
}
[780] Fix | Delete
}
[781] Fix | Delete
}
[782] Fix | Delete
[783] Fix | Delete
return null;
[784] Fix | Delete
}
[785] Fix | Delete
[786] Fix | Delete
/**
[787] Fix | Delete
* Changes default order status to draft for orders created via this API.
[788] Fix | Delete
*
[789] Fix | Delete
* @return string
[790] Fix | Delete
*/
[791] Fix | Delete
public function default_order_status() {
[792] Fix | Delete
return 'checkout-draft';
[793] Fix | Delete
}
[794] Fix | Delete
[795] Fix | Delete
/**
[796] Fix | Delete
* Create order line items.
[797] Fix | Delete
*
[798] Fix | Delete
* @param \WC_Order $order The order object to update.
[799] Fix | Delete
*/
[800] Fix | Delete
protected function update_line_items_from_cart( \WC_Order $order ) {
[801] Fix | Delete
$cart_controller = new CartController();
[802] Fix | Delete
$cart = $cart_controller->get_cart_instance();
[803] Fix | Delete
$cart_hashes = $cart_controller->get_cart_hashes();
[804] Fix | Delete
[805] Fix | Delete
if ( $order->get_cart_hash() !== $cart_hashes['line_items'] ) {
[806] Fix | Delete
$order->set_cart_hash( $cart_hashes['line_items'] );
[807] Fix | Delete
$order->remove_order_items( 'line_item' );
[808] Fix | Delete
wc()->checkout->create_order_line_items( $order, $cart );
[809] Fix | Delete
}
[810] Fix | Delete
[811] Fix | Delete
if ( $order->get_meta( '_shipping_hash' ) !== $cart_hashes['shipping'] ) {
[812] Fix | Delete
$order->update_meta_data( '_shipping_hash', $cart_hashes['shipping'] );
[813] Fix | Delete
$order->remove_order_items( 'shipping' );
[814] Fix | Delete
wc()->checkout->create_order_shipping_lines( $order, wc()->session->get( 'chosen_shipping_methods' ), wc()->shipping()->get_packages() );
[815] Fix | Delete
}
[816] Fix | Delete
[817] Fix | Delete
if ( $order->get_meta( '_coupons_hash' ) !== $cart_hashes['coupons'] ) {
[818] Fix | Delete
$order->remove_order_items( 'coupon' );
[819] Fix | Delete
$order->update_meta_data( '_coupons_hash', $cart_hashes['coupons'] );
[820] Fix | Delete
wc()->checkout->create_order_coupon_lines( $order, $cart );
[821] Fix | Delete
}
[822] Fix | Delete
[823] Fix | Delete
if ( $order->get_meta( '_fees_hash' ) !== $cart_hashes['fees'] ) {
[824] Fix | Delete
$order->update_meta_data( '_fees_hash', $cart_hashes['fees'] );
[825] Fix | Delete
$order->remove_order_items( 'fee' );
[826] Fix | Delete
wc()->checkout->create_order_fee_lines( $order, $cart );
[827] Fix | Delete
}
[828] Fix | Delete
[829] Fix | Delete
if ( $order->get_meta( '_taxes_hash' ) !== $cart_hashes['taxes'] ) {
[830] Fix | Delete
$order->update_meta_data( '_taxes_hash', $cart_hashes['taxes'] );
[831] Fix | Delete
$order->remove_order_items( 'tax' );
[832] Fix | Delete
wc()->checkout->create_order_tax_lines( $order, $cart );
[833] Fix | Delete
}
[834] Fix | Delete
}
[835] Fix | Delete
[836] Fix | Delete
/**
[837] Fix | Delete
* Update address data from cart and/or customer session data.
[838] Fix | Delete
*
[839] Fix | Delete
* @param \WC_Order $order The order object to update.
[840] Fix | Delete
*/
[841] Fix | Delete
protected function update_addresses_from_cart( \WC_Order $order ) {
[842] Fix | Delete
$order->set_props(
[843] Fix | Delete
array(
[844] Fix | Delete
'billing_first_name' => wc()->customer->get_billing_first_name(),
[845] Fix | Delete
'billing_last_name' => wc()->customer->get_billing_last_name(),
[846] Fix | Delete
'billing_company' => wc()->customer->get_billing_company(),
[847] Fix | Delete
'billing_address_1' => wc()->customer->get_billing_address_1(),
[848] Fix | Delete
'billing_address_2' => wc()->customer->get_billing_address_2(),
[849] Fix | Delete
'billing_city' => wc()->customer->get_billing_city(),
[850] Fix | Delete
'billing_state' => wc()->customer->get_billing_state(),
[851] Fix | Delete
'billing_postcode' => wc()->customer->get_billing_postcode(),
[852] Fix | Delete
'billing_country' => wc()->customer->get_billing_country(),
[853] Fix | Delete
'billing_email' => wc()->customer->get_billing_email(),
[854] Fix | Delete
'billing_phone' => wc()->customer->get_billing_phone(),
[855] Fix | Delete
'shipping_first_name' => wc()->customer->get_shipping_first_name(),
[856] Fix | Delete
'shipping_last_name' => wc()->customer->get_shipping_last_name(),
[857] Fix | Delete
'shipping_company' => wc()->customer->get_shipping_company(),
[858] Fix | Delete
'shipping_address_1' => wc()->customer->get_shipping_address_1(),
[859] Fix | Delete
'shipping_address_2' => wc()->customer->get_shipping_address_2(),
[860] Fix | Delete
'shipping_city' => wc()->customer->get_shipping_city(),
[861] Fix | Delete
'shipping_state' => wc()->customer->get_shipping_state(),
[862] Fix | Delete
'shipping_postcode' => wc()->customer->get_shipping_postcode(),
[863] Fix | Delete
'shipping_country' => wc()->customer->get_shipping_country(),
[864] Fix | Delete
'shipping_phone' => wc()->customer->get_shipping_phone(),
[865] Fix | Delete
)
[866] Fix | Delete
);
[867] Fix | Delete
$this->additional_fields_controller->sync_order_additional_fields_with_customer( $order, wc()->customer );
[868] Fix | Delete
}
[869] Fix | Delete
}
[870] Fix | Delete
[871] Fix | Delete
12
It is recommended that you Edit text format, this type of Fix handles quite a lot in one request
Function