ries->get_allowed_countries() ), ] ); } if ( $needs_shipping ) { $this->validate_address_fields( $shipping_address, 'shipping', $errors ); } $this->validate_address_fields( $billing_address, 'billing', $errors ); if ( ! $errors->has_errors() ) { return; } $errors_by_code = []; $error_codes = $errors->get_error_codes(); foreach ( $error_codes as $code ) { $errors_by_code[ $code ] = $errors->get_error_messages( $code ); } // Surface errors from first code. foreach ( $errors_by_code as $code => $error_messages ) { throw new RouteException( 'woocommerce_rest_invalid_address', sprintf( /* translators: %s Address type. */ __( 'There was a problem with the provided %s:', 'woocommerce' ) . ' ' . implode( ', ', $error_messages ), 'shipping' === $code ? __( 'shipping address', 'woocommerce' ) : __( 'billing address', 'woocommerce' ) ), 400, [ 'errors' => $errors_by_code, ] ); } } /** * Check all required address fields are set and return errors if not. * * @param string $country Country code. * @param array $allowed_countries List of valid country codes. * @return boolean True if valid. */ protected function validate_allowed_country( $country, array $allowed_countries ) { return array_key_exists( $country, $allowed_countries ); } /** * Check all required address fields are set and return errors if not. * * @param array $address Address array. * @param string $address_type billing or shipping address, used in error messages. * @param \WP_Error $errors Error object. */ protected function validate_address_fields( $address, $address_type, \WP_Error $errors ) { $all_locales = wc()->countries->get_country_locale(); $current_locale = isset( $all_locales[ $address['country'] ] ) ? $all_locales[ $address['country'] ] : []; /** * We are not using wc()->counties->get_default_address_fields() here because that is filtered. Instead, this array * is based on assets/js/base/components/cart-checkout/address-form/default-address-fields.js */ $address_fields = [ 'first_name' => [ 'label' => __( 'First name', 'woocommerce' ), 'required' => true, ], 'last_name' => [ 'label' => __( 'Last name', 'woocommerce' ), 'required' => true, ], 'company' => [ 'label' => __( 'Company', 'woocommerce' ), 'required' => false, ], 'address_1' => [ 'label' => __( 'Address', 'woocommerce' ), 'required' => true, ], 'address_2' => [ 'label' => __( 'Apartment, suite, etc.', 'woocommerce' ), 'required' => false, ], 'country' => [ 'label' => __( 'Country/Region', 'woocommerce' ), 'required' => true, ], 'city' => [ 'label' => __( 'City', 'woocommerce' ), 'required' => true, ], 'state' => [ 'label' => __( 'State/County', 'woocommerce' ), 'required' => true, ], 'postcode' => [ 'label' => __( 'Postal code', 'woocommerce' ), 'required' => true, ], ]; if ( $current_locale ) { foreach ( $current_locale as $key => $field ) { if ( isset( $address_fields[ $key ] ) ) { $address_fields[ $key ]['label'] = isset( $field['label'] ) ? $field['label'] : $address_fields[ $key ]['label']; $address_fields[ $key ]['required'] = isset( $field['required'] ) ? $field['required'] : $address_fields[ $key ]['required']; } } } foreach ( $address_fields as $address_field_key => $address_field ) { if ( empty( $address[ $address_field_key ] ) && $address_field['required'] ) { /* translators: %s Field label. */ $errors->add( $address_type, sprintf( __( '%s is required', 'woocommerce' ), $address_field['label'] ), $address_field_key ); } } } /** * Check email restrictions of a coupon against the order. * * @throws Exception Exception if invalid data is detected. * @param \WC_Coupon $coupon Coupon object applied to the cart. * @param \WC_Order $order Order object. */ protected function validate_coupon_email_restriction( \WC_Coupon $coupon, \WC_Order $order ) { $restrictions = $coupon->get_email_restrictions(); if ( ! empty( $restrictions ) && $order->get_billing_email() && ! wc()->cart->is_coupon_emails_allowed( [ $order->get_billing_email() ], $restrictions ) ) { throw new Exception( $coupon->get_coupon_error( \WC_Coupon::E_WC_COUPON_NOT_YOURS_REMOVED ) ); } } /** * Check usage restrictions of a coupon against the order. * * @throws Exception Exception if invalid data is detected. * @param \WC_Coupon $coupon Coupon object applied to the cart. * @param \WC_Order $order Order object. */ protected function validate_coupon_usage_limit( \WC_Coupon $coupon, \WC_Order $order ) { $coupon_usage_limit = $coupon->get_usage_limit_per_user(); if ( $coupon_usage_limit > 0 ) { $data_store = $coupon->get_data_store(); $usage_count = $order->get_customer_id() ? $data_store->get_usage_by_user_id( $coupon, $order->get_customer_id() ) : $data_store->get_usage_by_email( $coupon, $order->get_billing_email() ); if ( $usage_count >= $coupon_usage_limit ) { throw new Exception( $coupon->get_coupon_error( \WC_Coupon::E_WC_COUPON_USAGE_LIMIT_REACHED ) ); } } } /** * Changes default order status to draft for orders created via this API. * * @return string */ public function default_order_status() { return 'checkout-draft'; } /** * Create order line items. * * @param \WC_Order $order The order object to update. */ protected function update_line_items_from_cart( \WC_Order $order ) { $cart_controller = new CartController(); $cart = $cart_controller->get_cart_instance(); $cart_hashes = $cart_controller->get_cart_hashes(); if ( $order->get_cart_hash() !== $cart_hashes['line_items'] ) { $order->set_cart_hash( $cart_hashes['line_items'] ); $order->remove_order_items( 'line_item' ); wc()->checkout->create_order_line_items( $order, $cart ); } if ( $order->get_meta_data( '_shipping_hash' ) !== $cart_hashes['shipping'] ) { $order->update_meta_data( '_shipping_hash', $cart_hashes['shipping'] ); $order->remove_order_items( 'shipping' ); wc()->checkout->create_order_shipping_lines( $order, wc()->session->get( 'chosen_shipping_methods' ), wc()->shipping()->get_packages() ); } if ( $order->get_meta_data( '_coupons_hash' ) !== $cart_hashes['coupons'] ) { $order->remove_order_items( 'coupon' ); $order->update_meta_data( '_coupons_hash', $cart_hashes['coupons'] ); wc()->checkout->create_order_coupon_lines( $order, $cart ); } if ( $order->get_meta_data( '_fees_hash' ) !== $cart_hashes['fees'] ) { $order->update_meta_data( '_fees_hash', $cart_hashes['fees'] ); $order->remove_order_items( 'fee' ); wc()->checkout->create_order_fee_lines( $order, $cart ); } if ( $order->get_meta_data( '_taxes_hash' ) !== $cart_hashes['taxes'] ) { $order->update_meta_data( '_taxes_hash', $cart_hashes['taxes'] ); $order->remove_order_items( 'tax' ); wc()->checkout->create_order_tax_lines( $order, $cart ); } } /** * Update address data from cart and/or customer session data. * * @param \WC_Order $order The order object to update. */ protected function update_addresses_from_cart( \WC_Order $order ) { $customer_billing = wc()->customer->get_billing(); $customer_billing = array_combine( array_map( function( $key ) { return 'billing_' . $key; }, array_keys( $customer_billing ) ), $customer_billing ); $order->set_props( $customer_billing ); $customer_shipping = wc()->customer->get_shipping(); $customer_shipping = array_combine( array_map( function( $key ) { return 'shipping_' . $key; }, array_keys( $customer_shipping ) ), $customer_shipping ); $order->set_props( $customer_shipping ); } }