diff --git a/includes/Api/OrdersController.php b/includes/Api/OrdersController.php index 16b841c..02d9fa3 100644 --- a/includes/Api/OrdersController.php +++ b/includes/Api/OrdersController.php @@ -6,6 +6,57 @@ use WP_REST_Response; use Automattic\WooCommerce\Utilities\OrderUtil; class OrdersController { + /** + * Sanitize and validate a text field + * Returns empty string if value is empty, whitespace-only, or invalid + */ + private static function sanitize_field( $value ) { + if ( ! isset( $value ) || $value === '' ) { + return ''; + } + + $sanitized = sanitize_text_field( $value ); + $trimmed = trim( $sanitized ); + + // Return empty if only whitespace or sanitization removed everything + return $trimmed !== '' ? $trimmed : ''; + } + + /** + * Sanitize phone number + * Removes non-numeric characters except +, spaces, and hyphens + * Returns empty string if result is invalid + */ + private static function sanitize_phone( $phone ) { + if ( ! isset( $phone ) || $phone === '' ) { + return ''; + } + + // Remove non-numeric characters except + and spaces/hyphens + $phone = preg_replace( '/[^0-9+\s-]/', '', $phone ); + $phone = trim( $phone ); + + // If result is empty or just symbols, return empty + if ( $phone === '' || preg_match( '/^[+\s-]+$/', $phone ) ) { + return ''; + } + + return $phone; + } + + /** + * Sanitize email + * Returns empty string if invalid email + */ + private static function sanitize_email_field( $email ) { + if ( ! isset( $email ) || $email === '' ) { + return ''; + } + + $sanitized = sanitize_email( $email ); + return is_email( $sanitized ) ? $sanitized : ''; + } + public static function init() { // Register action hook for delayed email sending add_action( 'woonoow/send_order_email', [ __CLASS__, 'send_order_email' ], 10, 1 ); @@ -594,31 +645,31 @@ class OrdersController { // === Billing === if ( is_array( $billing ) ) { $order->set_address( [ - 'first_name' => (string) ( $billing['first_name'] ?? '' ), - 'last_name' => (string) ( $billing['last_name'] ?? '' ), - 'email' => (string) ( $billing['email'] ?? '' ), - 'phone' => (string) ( $billing['phone'] ?? '' ), - 'address_1' => (string) ( $billing['address_1'] ?? '' ), - 'address_2' => (string) ( $billing['address_2'] ?? '' ), - 'city' => (string) ( $billing['city'] ?? '' ), - 'state' => (string) ( $billing['state'] ?? '' ), - 'postcode' => (string) ( $billing['postcode'] ?? '' ), - 'country' => (string) ( $billing['country'] ?? '' ), + 'first_name' => self::sanitize_field( $billing['first_name'] ?? '' ), + 'last_name' => self::sanitize_field( $billing['last_name'] ?? '' ), + 'email' => self::sanitize_email_field( $billing['email'] ?? '' ), + 'phone' => self::sanitize_phone( $billing['phone'] ?? '' ), + 'address_1' => self::sanitize_field( $billing['address_1'] ?? '' ), + 'address_2' => self::sanitize_field( $billing['address_2'] ?? '' ), + 'city' => self::sanitize_field( $billing['city'] ?? '' ), + 'state' => self::sanitize_field( $billing['state'] ?? '' ), + 'postcode' => self::sanitize_field( $billing['postcode'] ?? '' ), + 'country' => self::sanitize_field( $billing['country'] ?? '' ), ], 'billing' ); } // === Shipping === if ( is_array( $shipping ) ) { $order->set_address( [ - 'first_name' => (string) ( $shipping['first_name'] ?? ( $shipping['name'] ?? '' ) ), - 'last_name' => (string) ( $shipping['last_name'] ?? '' ), - 'phone' => (string) ( $shipping['phone'] ?? '' ), - 'address_1' => (string) ( $shipping['address_1'] ?? '' ), - 'address_2' => (string) ( $shipping['address_2'] ?? '' ), - 'city' => (string) ( $shipping['city'] ?? '' ), - 'state' => (string) ( $shipping['state'] ?? '' ), - 'postcode' => (string) ( $shipping['postcode'] ?? '' ), - 'country' => (string) ( $shipping['country'] ?? '' ), + 'first_name' => self::sanitize_field( $shipping['first_name'] ?? ( $shipping['name'] ?? '' ) ), + 'last_name' => self::sanitize_field( $shipping['last_name'] ?? '' ), + 'phone' => self::sanitize_phone( $shipping['phone'] ?? '' ), + 'address_1' => self::sanitize_field( $shipping['address_1'] ?? '' ), + 'address_2' => self::sanitize_field( $shipping['address_2'] ?? '' ), + 'city' => self::sanitize_field( $shipping['city'] ?? '' ), + 'state' => self::sanitize_field( $shipping['state'] ?? '' ), + 'postcode' => self::sanitize_field( $shipping['postcode'] ?? '' ), + 'country' => self::sanitize_field( $shipping['country'] ?? '' ), ], 'shipping' ); } @@ -858,16 +909,16 @@ class OrdersController { // Billing if ( $billing ) { $order->set_address( [ - 'first_name' => (string) ( $billing['first_name'] ?? '' ), - 'last_name' => (string) ( $billing['last_name'] ?? '' ), - 'email' => (string) ( $billing['email'] ?? '' ), - 'phone' => (string) ( $billing['phone'] ?? '' ), - 'address_1' => (string) ( $billing['address_1'] ?? '' ), - 'address_2' => (string) ( $billing['address_2'] ?? '' ), - 'city' => (string) ( $billing['city'] ?? '' ), - 'state' => (string) ( $billing['state'] ?? '' ), - 'postcode' => (string) ( $billing['postcode'] ?? '' ), - 'country' => (string) ( $billing['country'] ?? '' ), + 'first_name' => self::sanitize_field( $billing['first_name'] ?? '' ), + 'last_name' => self::sanitize_field( $billing['last_name'] ?? '' ), + 'email' => self::sanitize_email_field( $billing['email'] ?? '' ), + 'phone' => self::sanitize_phone( $billing['phone'] ?? '' ), + 'address_1' => self::sanitize_field( $billing['address_1'] ?? '' ), + 'address_2' => self::sanitize_field( $billing['address_2'] ?? '' ), + 'city' => self::sanitize_field( $billing['city'] ?? '' ), + 'state' => self::sanitize_field( $billing['state'] ?? '' ), + 'postcode' => self::sanitize_field( $billing['postcode'] ?? '' ), + 'country' => self::sanitize_field( $billing['country'] ?? '' ), ], 'billing' ); } @@ -876,15 +927,15 @@ class OrdersController { $ship_addr = $ship_to_different ? $shipping : $billing; if ( $ship_addr ) { $order->set_address( [ - 'first_name' => (string) ( $ship_addr['first_name'] ?? ( $ship_addr['name'] ?? '' ) ), - 'last_name' => (string) ( $ship_addr['last_name'] ?? '' ), - 'phone' => (string) ( $ship_addr['phone'] ?? '' ), - 'address_1' => (string) ( $ship_addr['address_1'] ?? '' ), - 'address_2' => (string) ( $ship_addr['address_2'] ?? '' ), - 'city' => (string) ( $ship_addr['city'] ?? '' ), - 'state' => (string) ( $ship_addr['state'] ?? '' ), - 'postcode' => (string) ( $ship_addr['postcode'] ?? '' ), - 'country' => (string) ( $ship_addr['country'] ?? '' ), + 'first_name' => self::sanitize_field( $ship_addr['first_name'] ?? ( $ship_addr['name'] ?? '' ) ), + 'last_name' => self::sanitize_field( $ship_addr['last_name'] ?? '' ), + 'phone' => self::sanitize_phone( $ship_addr['phone'] ?? '' ), + 'address_1' => self::sanitize_field( $ship_addr['address_1'] ?? '' ), + 'address_2' => self::sanitize_field( $ship_addr['address_2'] ?? '' ), + 'city' => self::sanitize_field( $ship_addr['city'] ?? '' ), + 'state' => self::sanitize_field( $ship_addr['state'] ?? '' ), + 'postcode' => self::sanitize_field( $ship_addr['postcode'] ?? '' ), + 'country' => self::sanitize_field( $ship_addr['country'] ?? '' ), ], 'shipping' ); } @@ -1035,26 +1086,49 @@ class OrdersController { // Update customer billing & shipping data $customer = new \WC_Customer( $user->ID ); - // Update billing address - if ( ! empty( $billing['first_name'] ) ) $customer->set_billing_first_name( $billing['first_name'] ); - if ( ! empty( $billing['last_name'] ) ) $customer->set_billing_last_name( $billing['last_name'] ); - if ( ! empty( $billing['email'] ) ) $customer->set_billing_email( $billing['email'] ); - if ( ! empty( $billing['phone'] ) ) $customer->set_billing_phone( $billing['phone'] ); - if ( ! empty( $billing['address_1'] ) ) $customer->set_billing_address_1( $billing['address_1'] ); - if ( ! empty( $billing['city'] ) ) $customer->set_billing_city( $billing['city'] ); - if ( ! empty( $billing['state'] ) ) $customer->set_billing_state( $billing['state'] ); - if ( ! empty( $billing['postcode'] ) ) $customer->set_billing_postcode( $billing['postcode'] ); - if ( ! empty( $billing['country'] ) ) $customer->set_billing_country( $billing['country'] ); + // Update billing address - sanitize and validate each field + $first_name = self::sanitize_field( $billing['first_name'] ?? '' ); + $last_name = self::sanitize_field( $billing['last_name'] ?? '' ); + $email = self::sanitize_email_field( $billing['email'] ?? '' ); + $phone = self::sanitize_phone( $billing['phone'] ?? '' ); + $address_1 = self::sanitize_field( $billing['address_1'] ?? '' ); + $address_2 = self::sanitize_field( $billing['address_2'] ?? '' ); + $city = self::sanitize_field( $billing['city'] ?? '' ); + $state = self::sanitize_field( $billing['state'] ?? '' ); + $postcode = self::sanitize_field( $billing['postcode'] ?? '' ); + $country = self::sanitize_field( $billing['country'] ?? '' ); + + // Only set if not empty (allow clearing fields by passing empty string) + if ( $first_name !== '' ) $customer->set_billing_first_name( $first_name ); + if ( $last_name !== '' ) $customer->set_billing_last_name( $last_name ); + if ( $email !== '' ) $customer->set_billing_email( $email ); + if ( $phone !== '' ) $customer->set_billing_phone( $phone ); + if ( $address_1 !== '' ) $customer->set_billing_address_1( $address_1 ); + if ( $address_2 !== '' ) $customer->set_billing_address_2( $address_2 ); + if ( $city !== '' ) $customer->set_billing_city( $city ); + if ( $state !== '' ) $customer->set_billing_state( $state ); + if ( $postcode !== '' ) $customer->set_billing_postcode( $postcode ); + if ( $country !== '' ) $customer->set_billing_country( $country ); // Update shipping address (if provided) if ( ! empty( $shipping ) && is_array( $shipping ) ) { - if ( ! empty( $shipping['first_name'] ) ) $customer->set_shipping_first_name( $shipping['first_name'] ); - if ( ! empty( $shipping['last_name'] ) ) $customer->set_shipping_last_name( $shipping['last_name'] ); - if ( ! empty( $shipping['address_1'] ) ) $customer->set_shipping_address_1( $shipping['address_1'] ); - if ( ! empty( $shipping['city'] ) ) $customer->set_shipping_city( $shipping['city'] ); - if ( ! empty( $shipping['state'] ) ) $customer->set_shipping_state( $shipping['state'] ); - if ( ! empty( $shipping['postcode'] ) ) $customer->set_shipping_postcode( $shipping['postcode'] ); - if ( ! empty( $shipping['country'] ) ) $customer->set_shipping_country( $shipping['country'] ); + $s_first_name = self::sanitize_field( $shipping['first_name'] ?? '' ); + $s_last_name = self::sanitize_field( $shipping['last_name'] ?? '' ); + $s_address_1 = self::sanitize_field( $shipping['address_1'] ?? '' ); + $s_address_2 = self::sanitize_field( $shipping['address_2'] ?? '' ); + $s_city = self::sanitize_field( $shipping['city'] ?? '' ); + $s_state = self::sanitize_field( $shipping['state'] ?? '' ); + $s_postcode = self::sanitize_field( $shipping['postcode'] ?? '' ); + $s_country = self::sanitize_field( $shipping['country'] ?? '' ); + + if ( $s_first_name !== '' ) $customer->set_shipping_first_name( $s_first_name ); + if ( $s_last_name !== '' ) $customer->set_shipping_last_name( $s_last_name ); + if ( $s_address_1 !== '' ) $customer->set_shipping_address_1( $s_address_1 ); + if ( $s_address_2 !== '' ) $customer->set_shipping_address_2( $s_address_2 ); + if ( $s_city !== '' ) $customer->set_shipping_city( $s_city ); + if ( $s_state !== '' ) $customer->set_shipping_state( $s_state ); + if ( $s_postcode !== '' ) $customer->set_shipping_postcode( $s_postcode ); + if ( $s_country !== '' ) $customer->set_shipping_country( $s_country ); } $customer->save(); @@ -1084,26 +1158,49 @@ class OrdersController { // Set WooCommerce customer billing data $customer = new \WC_Customer( $user_id ); - // Billing address - if ( ! empty( $billing['first_name'] ) ) $customer->set_billing_first_name( $billing['first_name'] ); - if ( ! empty( $billing['last_name'] ) ) $customer->set_billing_last_name( $billing['last_name'] ); - if ( ! empty( $billing['email'] ) ) $customer->set_billing_email( $billing['email'] ); - if ( ! empty( $billing['phone'] ) ) $customer->set_billing_phone( $billing['phone'] ); - if ( ! empty( $billing['address_1'] ) ) $customer->set_billing_address_1( $billing['address_1'] ); - if ( ! empty( $billing['city'] ) ) $customer->set_billing_city( $billing['city'] ); - if ( ! empty( $billing['state'] ) ) $customer->set_billing_state( $billing['state'] ); - if ( ! empty( $billing['postcode'] ) ) $customer->set_billing_postcode( $billing['postcode'] ); - if ( ! empty( $billing['country'] ) ) $customer->set_billing_country( $billing['country'] ); + // Billing address - sanitize and validate each field + $first_name = self::sanitize_field( $billing['first_name'] ?? '' ); + $last_name = self::sanitize_field( $billing['last_name'] ?? '' ); + $email = self::sanitize_email_field( $billing['email'] ?? '' ); + $phone = self::sanitize_phone( $billing['phone'] ?? '' ); + $address_1 = self::sanitize_field( $billing['address_1'] ?? '' ); + $address_2 = self::sanitize_field( $billing['address_2'] ?? '' ); + $city = self::sanitize_field( $billing['city'] ?? '' ); + $state = self::sanitize_field( $billing['state'] ?? '' ); + $postcode = self::sanitize_field( $billing['postcode'] ?? '' ); + $country = self::sanitize_field( $billing['country'] ?? '' ); + + // Only set if not empty + if ( $first_name !== '' ) $customer->set_billing_first_name( $first_name ); + if ( $last_name !== '' ) $customer->set_billing_last_name( $last_name ); + if ( $email !== '' ) $customer->set_billing_email( $email ); + if ( $phone !== '' ) $customer->set_billing_phone( $phone ); + if ( $address_1 !== '' ) $customer->set_billing_address_1( $address_1 ); + if ( $address_2 !== '' ) $customer->set_billing_address_2( $address_2 ); + if ( $city !== '' ) $customer->set_billing_city( $city ); + if ( $state !== '' ) $customer->set_billing_state( $state ); + if ( $postcode !== '' ) $customer->set_billing_postcode( $postcode ); + if ( $country !== '' ) $customer->set_billing_country( $country ); // Shipping address (if provided) if ( ! empty( $shipping ) && is_array( $shipping ) ) { - if ( ! empty( $shipping['first_name'] ) ) $customer->set_shipping_first_name( $shipping['first_name'] ); - if ( ! empty( $shipping['last_name'] ) ) $customer->set_shipping_last_name( $shipping['last_name'] ); - if ( ! empty( $shipping['address_1'] ) ) $customer->set_shipping_address_1( $shipping['address_1'] ); - if ( ! empty( $shipping['city'] ) ) $customer->set_shipping_city( $shipping['city'] ); - if ( ! empty( $shipping['state'] ) ) $customer->set_shipping_state( $shipping['state'] ); - if ( ! empty( $shipping['postcode'] ) ) $customer->set_shipping_postcode( $shipping['postcode'] ); - if ( ! empty( $shipping['country'] ) ) $customer->set_shipping_country( $shipping['country'] ); + $s_first_name = self::sanitize_field( $shipping['first_name'] ?? '' ); + $s_last_name = self::sanitize_field( $shipping['last_name'] ?? '' ); + $s_address_1 = self::sanitize_field( $shipping['address_1'] ?? '' ); + $s_address_2 = self::sanitize_field( $shipping['address_2'] ?? '' ); + $s_city = self::sanitize_field( $shipping['city'] ?? '' ); + $s_state = self::sanitize_field( $shipping['state'] ?? '' ); + $s_postcode = self::sanitize_field( $shipping['postcode'] ?? '' ); + $s_country = self::sanitize_field( $shipping['country'] ?? '' ); + + if ( $s_first_name !== '' ) $customer->set_shipping_first_name( $s_first_name ); + if ( $s_last_name !== '' ) $customer->set_shipping_last_name( $s_last_name ); + if ( $s_address_1 !== '' ) $customer->set_shipping_address_1( $s_address_1 ); + if ( $s_address_2 !== '' ) $customer->set_shipping_address_2( $s_address_2 ); + if ( $s_city !== '' ) $customer->set_shipping_city( $s_city ); + if ( $s_state !== '' ) $customer->set_shipping_state( $s_state ); + if ( $s_postcode !== '' ) $customer->set_shipping_postcode( $s_postcode ); + if ( $s_country !== '' ) $customer->set_shipping_country( $s_country ); } $customer->save();