handle_data(); } catch ( Exception $error ) { $this->logger->error( 'Cart ' . $this->logger_tag . ' failed: ' . $error->getMessage() ); wp_send_json_error( array( 'name' => is_a( $error, PayPalApiException::class ) ? $error->name() : '', 'message' => $error->getMessage(), 'code' => $error->getCode(), 'details' => is_a( $error, PayPalApiException::class ) ? $error->details() : array(), ) ); return false; } } /** * Handles the request data. * * @return bool * @throws Exception On error. */ abstract protected function handle_data(): bool; /** * Adds products to cart. * * @param array $products Array of products to be added to cart. * @return bool * @throws Exception */ protected function add_products( array $products ): bool { $this->cart->empty_cart( false ); $success = true; foreach ( $products as $product ) { if ( $product['product']->is_type( 'booking' ) ) { $success = $success && $this->add_booking_product( $product['product'], $product['booking'] ); } elseif ( $product['product']->is_type( 'variable' ) ) { $success = $success && $this->add_variable_product( $product['product'], $product['quantity'], $product['variations'] ); } else { $success = $success && $this->add_product( $product['product'], $product['quantity'] ); } } if ( ! $success ) { $this->handle_error(); } return $success; } /** * Handles errors. * * @return void */ private function handle_error(): void { $message = __( 'Something went wrong. Action aborted', 'woocommerce-paypal-payments' ); $errors = wc_get_notices( 'error' ); if ( count( $errors ) ) { $message = array_reduce( $errors, static function ( string $add, array $error ): string { return $add . $error['notice'] . ' '; }, '' ); wc_clear_notices(); } wp_send_json_error( array( 'name' => '', 'message' => $message, 'code' => 0, 'details' => array(), ) ); } /** * @return array|false */ protected function products_from_request() { $data = $this->request_data->read_request( $this->nonce() ); $products = $this->products_from_data( $data ); if ( ! $products ) { wp_send_json_error( array( 'name' => '', 'message' => __( 'Necessary fields not defined. Action aborted.', 'woocommerce-paypal-payments' ), 'code' => 0, 'details' => array(), ) ); return false; } return $products; } /** * Returns product information from a data array. * * @param array $data The data array. * * @return array|null */ protected function products_from_data( array $data ): ?array { $products = array(); if ( ! isset( $data['products'] ) || ! is_array( $data['products'] ) ) { return null; } foreach ( $data['products'] as $product ) { if ( ! isset( $product['quantity'] ) || ! isset( $product['id'] ) ) { return null; } $wc_product = wc_get_product( (int) $product['id'] ); if ( ! $wc_product ) { return null; } $products[] = array( 'product' => $wc_product, 'quantity' => (int) $product['quantity'], 'variations' => $product['variations'] ?? null, 'booking' => $product['booking'] ?? null, ); } return $products; } /** * Adds a product to the cart. * * @param \WC_Product $product The Product. * @param int $quantity The Quantity. * * @return bool * @throws Exception When product could not be added. */ private function add_product( \WC_Product $product, int $quantity ): bool { $cart_item_key = $this->cart->add_to_cart( $product->get_id(), $quantity ); $this->cart_item_keys[] = $cart_item_key; return false !== $cart_item_key; } /** * Adds variations to the cart. * * @param \WC_Product $product The Product. * @param int $quantity The Quantity. * @param array $post_variations The variations. * * @return bool * @throws Exception When product could not be added. */ private function add_variable_product( \WC_Product $product, int $quantity, array $post_variations ): bool { $variations = array(); foreach ( $post_variations as $key => $value ) { $variations[ $value['name'] ] = $value['value']; } $variation_id = $this->product_data_store->find_matching_product_variation( $product, $variations ); // ToDo: Check stock status for variation. $cart_item_key = $this->cart->add_to_cart( $product->get_id(), $quantity, $variation_id, $variations ); $this->cart_item_keys[] = $cart_item_key; return false !== $cart_item_key; } /** * Adds booking to the cart. * * @param \WC_Product $product The Product. * @param array $data Data used by the booking plugin. * * @return bool * @throws Exception When product could not be added. */ private function add_booking_product( \WC_Product $product, array $data ): bool { if ( ! is_callable( 'wc_bookings_get_posted_data' ) ) { return false; } $cart_item_data = array( 'booking' => wc_bookings_get_posted_data( $data, $product ), ); $cart_item_key = $this->cart->add_to_cart( $product->get_id(), 1, 0, array(), $cart_item_data ); $this->cart_item_keys[] = $cart_item_key; return false !== $cart_item_key; } /** * @return void */ protected function remove_cart_items(): void { foreach ( $this->cart_item_keys as $cart_item_key ) { $this->cart->remove_cart_item( $cart_item_key ); } } }