namespace, '/' . $this->rest_base, [ [ 'methods' => WP_REST_Server::READABLE, 'callback' => [ $this, 'get_items' ], 'permission_callback' => [ $this, 'permissions_check' ], 'args' => $this->get_collection_params(), ], [ 'methods' => WP_REST_Server::CREATABLE, 'callback' => [ $this, 'create_item' ], 'permission_callback' => [ $this, 'permissions_check' ], 'args' => $this->get_post_item_schema(), ], 'schema' => [ $this, 'get_public_item_schema' ], ] ); register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P[\d]+)', [ 'args' => [ 'id' => [ 'description' => __( 'Unique identifier for the object.', 'storeengine' ), 'type' => 'integer', ], ], [ 'methods' => WP_REST_Server::READABLE, 'callback' => [ $this, 'get_item' ], 'permission_callback' => [ $this, 'permissions_check' ], 'args' => [ 'context' => $this->get_context_param( [ 'default' => 'view' ] ), ], ], [ 'methods' => WP_REST_Server::EDITABLE, 'callback' => [ $this, 'update_item' ], 'permission_callback' => [ $this, 'permissions_check' ], 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ), ], [ 'methods' => WP_REST_Server::DELETABLE, 'callback' => [ $this, 'delete_item' ], 'permission_callback' => [ $this, 'permissions_check' ], ], 'schema' => [ $this, 'get_item_schema' ], ] ); register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P[\d]+)/order_item', [ 'args' => [ 'id' => [ 'description' => __( 'Unique identifier for the object.', 'storeengine' ), 'type' => 'integer', ], ], [ 'methods' => WP_REST_Server::CREATABLE, 'callback' => [ $this, 'add_order_item' ], 'permission_callback' => [ $this, 'permissions_check' ], 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ), ], [ 'methods' => WP_REST_Server::DELETABLE, 'callback' => [ $this, 'delete_order_item' ], 'permission_callback' => [ $this, 'permissions_check' ], 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ), ], ] ); register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P[\d]+)/order_item/(?P[\d]+)', [ 'args' => [ 'id' => [ 'description' => __( 'Unique identifier for the order.', 'storeengine' ), 'type' => 'integer', ], 'item_id' => [ 'description' => __( 'Unique identifier for the order item.', 'storeengine' ), 'type' => 'integer', ], ], [ 'methods' => WP_REST_Server::EDITABLE, 'callback' => [ $this, 'update_order_item' ], 'permission_callback' => [ $this, 'permissions_check' ], 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ), ], ] ); // @TODO move to common api. register_rest_route( $this->namespace, '/' . $this->rest_base . '/get_states/(?P[\S]{2})', [ 'args' => [ 'cc' => [ 'description' => __( 'ISO 3166-1 alpha-2 (two-letter) country code.', 'storeengine' ), 'type' => 'string', 'required' => true, ], ], [ 'methods' => WP_REST_Server::READABLE, 'callback' => [ $this, 'get_states' ], 'permission_callback' => [ $this, 'permissions_check' ], 'args' => [ 'context' => $this->get_context_param( [ 'default' => 'view' ] ), ], ], 'schema' => [ $this, 'get_item_schema' ], ] ); } public function add_order_item( $request ) { $order_id = $request->get_param( 'id' ); $order = Helper::get_order( $order_id ); if ( is_wp_error( $order ) ) { return $order; } if ( ! $order ) { return new WP_Error( 'no-order', __( 'Order not found.', 'storeengine' ), [ 'status' => 404 ] ); } if ( ! $order->is_editable() ) { return new WP_Error( 'order-not-editable', __( 'Order is no longer editable.', 'storeengine' ), [ 'status' => 400 ] ); } $body = $request->get_json_params(); $type = $body['type'] ?? ''; switch ( $type ) { case 'product': if ( ! empty( $body['price_id'] ) ) { $price = new \StoreEngine\Classes\Price( absint( $body['price_id'] ) ); if ( ! $price->get_id() ) { return new WP_Error( 'product-not-found', __( 'Product not found.', 'storeengine' ) ); } $order_item = $order->add_product( $price, max( 1, absint( $body['quantity'] ?? 1 ) ), [ 'order' => $order, 'variation_id' => $body['variation'] ?? 0, ] ); if ( ! is_wp_error( $order_item ) ) { $order->maybe_set_digital_auto_complete(); } } else { $order_item = new WP_Error( 'invalid-request', __( 'Product Price ID is required.', 'storeengine' ), [ 'status' => 400 ] ); } break; case 'coupon': if ( ! empty( $body['code'] ) && ! StringUtil::is_null_or_whitespace( $body['code'] ) ) { $order_item = $order->apply_coupon( strtolower( $body['code'] ) ); } else { $order_item = new WP_Error( 'invalid-request', __( 'Coupon code is required.', 'storeengine' ), [ 'status' => 400 ] ); } break; case 'fee': if ( ! empty( $body['name'] ) && ! empty( $body['amount'] ) && (float) $body['amount'] > 0 ) { $order_item = $order->add_fee( $body['name'], (float) $body['amount'] ); } else { $order_item = new WP_Error( 'invalid-request', __( 'Fee name & amount is required.', 'storeengine' ), [ 'status' => 400 ] ); } break; default: $order_item = new WP_Error( 'invalid-request', __( 'Invalid request.', 'storeengine' ), [ 'status' => 400 ] ); break; } if ( is_wp_error( $order_item ) ) { return $order_item; } $order->recalculate_coupons(); $order->calculate(); $order->save(); do_action( 'storeengine/api/order/add_order_item', $order, $order_item, $type ); return rest_ensure_response( $this->prepare_item_for_response( $order, $request ) ); } public function update_order_item( $request ) { $order_id = $request->get_param( 'id' ); $item_id = $request->get_param( 'item_id' ); if ( ! $order_id || ! $item_id ) { return new WP_Error( 'invalid-request', __( 'Invalid request.', 'storeengine' ), [ 'status' => 400 ] ); } $order = Helper::get_order( $order_id ); if ( is_wp_error( $order ) ) { return $order; } if ( ! $order ) { return new WP_Error( 'no-order', __( 'Order not found.', 'storeengine' ), [ 'status' => 404 ] ); } if ( ! $order->is_editable() ) { return new WP_Error( 'order-not-editable', __( 'Order is no longer editable.', 'storeengine' ), [ 'status' => 400 ] ); } $item = $order->get_item( absint( $item_id ) ); if ( ! $item ) { return new WP_Error( 'no-order-item', __( 'Order item not found.', 'storeengine' ), [ 'status' => 404 ] ); } $body = $request->get_json_params(); $quantity = absint( $body['quantity'] ?? 0 ); if ( ! $quantity ) { return new WP_Error( 'no-order-item', __( 'Quantity is required.', 'storeengine' ), [ 'status' => 404 ] ); } $total = Formatting::get_price_excluding_tax( $item->get_price( 'edit' ), $item->get_price_id( 'edit' ), $item->get_product_id( 'edit' ), [ 'qty' => $quantity, 'order' => $order, ] ); $item->set_quantity( $quantity ); $item->set_subtotal( $total ); $item->set_total( $total ); $item->save(); $order->recalculate_coupons(); $order->calculate(); $order->save(); do_action( 'storeengine/api/order/update_order_item', $order, $item ); return rest_ensure_response( $this->prepare_item_for_response( $order, $request ) ); } public function delete_order_item( $request ) { $order_id = $request->get_param( 'id' ); $order = Helper::get_order( $order_id ); if ( ! $order ) { return new WP_Error( 'no-order', __( 'Order not found.', 'storeengine' ), [ 'status' => 404 ] ); } if ( is_wp_error( $order ) ) { return $order; } if ( ! $order->is_editable() ) { return new WP_Error( 'order-not-editable', __( 'Order is no longer editable.', 'storeengine' ), [ 'status' => 400 ] ); } $body = $request->get_json_params(); if ( empty( $body['item_id'] ) ) { return new WP_Error( 'no-item', __( 'Order item not found.', 'storeengine' ), [ 'status' => 404 ] ); } $item_id = absint( $body['item_id'] ); $item = $order->get_item( $item_id, false ); $removed = $order->remove_item( $item_id ); if ( ! $removed ) { return new WP_Error( 'failed-to-remove', __( 'Failed to remove order item. Maybe order item already removed.', 'storeengine' ), [ 'status' => 404 ] ); } if ( $removed->is_type( 'line_item' ) ) { $order->maybe_set_digital_auto_complete(); } $order->recalculate_coupons(); $order->calculate(); $order->save(); do_action( 'storeengine/api/order/delete_order_item', $order, $item, $item_id ); return rest_ensure_response( $this->prepare_item_for_response( $order, $request ) ); } /** * @param WP_REST_Request $request Full details about the request. * * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. * @throws StoreEngineInvalidArgumentException */ public function get_items( $request ) { $page = max( 1, absint( $request->get_param( 'page' ) ) ); $status = $request->get_param( 'status' ); $search = $request->get_param( 'search' ); $customer = absint( $request->get_param( 'customer' ) ); $where = [ 'relation' => 'AND', [ 'key' => 'type', 'value' => 'order', ], ]; if ( $status && ! in_array( $status, [ 'all', 'any', 'draft' ], true ) ) { $where[] = [ 'key' => 'status', 'value' => $status, ]; } else { $where[] = [ 'key' => 'status', 'value' => 'draft', 'compare' => '!=', ]; } if ( $customer ) { $where[] = [ 'key' => 'customer_id', 'value' => $customer, 'compare' => '=', 'type' => 'NUMERIC', ]; } if ( $search ) { if ( is_numeric( $search ) ) { $where[] = [ 'key' => 'id', 'value' => $search, ]; } elseif ( is_email( $search ) ) { $_email_where = [ 'relation' => 'OR', [ 'key' => 'billing_email', 'value' => $search, ], ]; if ( email_exists( $search ) ) { $_email_where[] = [ 'key' => 'customer_id', 'value' => email_exists( $search ), ]; } $where[] = $_email_where; } else { $where[] = [ 'relation' => 'OR', [ 'key' => 'billing_email', 'value' => "%$search%", 'compare' => 'LIKE', ], [ 'key' => 'payment_method', 'value' => $search, ], [ 'key' => 'payment_method_title', 'value' => "%$search%", 'compare' => 'LIKE', ], ]; } } $query = new OrderCollection( [ 'per_page' => absint( $request->get_param( 'per_page' ) ), 'page' => $page, 'where' => $where, ] ); $data = []; foreach ( $query->get_results() as $order ) { $response = $this->prepare_item_for_response( $order, $request ); $data[] = $this->prepare_response_for_collection( $response ); } return $this->prepare_query_response( $data, $query, $request ); } /** * Prepare links for the request. * * @param StoreEngineOrder $item * @param WP_REST_Request $request Request object. * * @return array Links for the given order. */ protected function prepare_links( $item, WP_REST_Request $request ): array { $links = parent::prepare_links( $item, $request ); if ( $item->get_user_id() ) { $links['customer'] = [ 'href' => rest_url( sprintf( '/%s/customers/%d', $this->namespace, $item->get_user_id() ) ), ]; } if ( $item->get_parent_id() ) { $links['up'] = [ 'href' => rest_url( sprintf( '/%s/orders/%d', $this->namespace, $item->get_parent_id() ) ), ]; } return $links; } /** * @param StoreEngineOrder $item Order object. * @param WP_REST_Request $request Requests. * * @return WP_Error|WP_HTTP_Response|WP_REST_Response */ public function prepare_item_for_response( $item, $request ) { $schema = $this->get_public_item_schema(); $data = [ 'id' => $item->get_id(), 'is_editable' => $item->is_editable(), 'currency' => $item->get_currency(), ]; if ( isset( $schema['properties']['status'] ) ) { $data['status'] = $item->get_status(); } $data['paid_status'] = $item->get_paid_status(); if ( $item->get_date_paid_gmt() ) { $data['date_paid_gmt'] = $item->get_date_paid_gmt()->format( 'Y-m-d H:i:s' ); } else { $data['date_paid_gmt'] = null; } if ( isset( $schema['properties']['type'] ) ) { $data['type'] = $item->get_type(); } if ( isset( $schema['properties']['tax_amount'] ) ) { $data['tax_amount'] = $item->get_tax_amount(); } if ( isset( $schema['properties']['refunds_total'] ) ) { $data['refunds_total'] = $item->get_total_refunded(); } // Refund capability fields consumed by the shared RefundModal (same shape as // the Payments page payment data). Computed via the same logic as get_payment_data. if ( isset( $schema['properties']['can_refund'] ) ) { $data['can_refund'] = false; $data['gateway_can_refund_order'] = false; $data['gateway_name'] = __( 'Payment gateway', 'storeengine' ); if ( $item->is_paid() && 'refunded' !== $item->get_status() ) { $data['can_refund'] = (bool) apply_filters( 'storeengine/refund/can_admin_refund_order', ( 0 < $item->get_total() - $item->get_total_refunded() ), $item->get_id(), $item ); $gateway = Helper::get_payment_gateway_by_order( $item ); if ( false !== $gateway ) { $data['gateway_name'] = ! empty( $gateway->method_title ) ? $gateway->method_title : $gateway->get_title(); $data['gateway_can_refund_order'] = (bool) $gateway->can_refund_order( $item ); } } } if ( isset( $schema['properties']['subtotal_amount'] ) ) { $data['subtotal_amount'] = $item->get_subtotal(); } if ( isset( $schema['properties']['date_created_gmt'] ) ) { if ( $item->get_date_created_gmt() ) { $data['date_created_gmt'] = $item->get_date_created_gmt()->format( 'Y-m-d H:i:s' ); } else { $data['date_created_gmt'] = null; } } if ( isset( $schema['properties']['order_placed_date_gmt'] ) ) { if ( $item->get_order_placed_date_gmt() ) { $data['order_placed_date_gmt'] = $item->get_order_placed_date_gmt()->format( 'Y-m-d H:i:s' ); } else { $data['order_placed_date_gmt'] = null; } } if ( isset( $schema['properties']['order_placed_date'] ) ) { if ( $item->get_order_placed_date() ) { $data['order_placed_date'] = $item->get_order_placed_date()->format( 'Y-m-d H:i:s' ); } else { $data['order_placed_date'] = null; } } if ( isset( $schema['properties']['coupons'] ) ) { $data['coupons'] = array_values( array_map( fn( $coupon ) => [ 'id' => $coupon->get_id(), 'code' => $coupon->get_name(), ], $item->get_coupons() ) ); $data['total_discount'] = $item->get_total_discount(); } $data['shipping_methods'] = array_values( array_map( fn( $shipping ) => [ 'name' => $shipping->get_name(), 'taxable' => $shipping->get_tax_status(), ], $item->get_shipping_methods() ) ); $data['shipping_total'] = $item->get_shipping_total(); $data['fees'] = array_values( array_map( fn( $fee ) => [ 'id' => $fee->get_id(), 'name' => $fee->get_name( 'edit' ), 'tax_class' => $fee->get_tax_class(), 'tax_status' => $fee->get_tax_status(), 'amount' => Formatting::round_tax_total( $fee->get_amount( 'edit' ) ), 'total' => $fee->get_total(), 'total_tax' => $fee->get_total_tax(), 'taxes' => $fee->get_taxes(), ], $item->get_fees() ) ); $data['taxes'] = array_values( array_map( fn( $tax ) => [ 'code' => $tax->code, 'label' => $tax->label, 'amount' => Formatting::round_tax_total( $tax->amount ), ], $item->get_tax_totals() ) ); if ( isset( $schema['properties']['total_amount'] ) ) { $data['total_amount'] = $item->get_total_amount(); $data['subtotal_amount'] = $item->get_subtotal(); } if ( isset( $schema['properties']['customer_id'] ) ) { $data['customer_id'] = $item->get_customer_id(); } // customer email and customer name if ( isset( $schema['properties']['customer_email'] ) ) { $data['customer_email'] = $item->get_billing_email(); $data['email_hash'] = md5( $item->get_billing_email() ); } if ( isset( $schema['properties']['customer_name'] ) ) { $data['customer_name'] = trim( $item->get_billing_first_name() . ' ' . $item->get_billing_last_name() ); } if ( isset( $schema['properties']['billing_email'] ) ) { $data['billing_email'] = $item->get_order_email(); } if ( isset( $schema['properties']['payment_method'] ) ) { $data['payment_method'] = $item->get_payment_method(); } if ( isset( $schema['properties']['payment_method_title'] ) ) { $data['payment_method_title'] = $item->get_payment_method_title(); } if ( isset( $schema['properties']['transaction_id'] ) ) { $data['transaction_id'] = $item->get_transaction_id(); } if ( isset( $schema['properties']['customer_note'] ) ) { $data['customer_note'] = $item->get_customer_note(); } $data['is_auto_complete_digital_order'] = $item->get_auto_complete_digital_order(); if ( isset( $schema['properties']['meta'] ) ) { $data['meta'] = $item->get_meta_data(); } // purchase_items if ( isset( $schema['properties']['purchase_items'] ) ) { global $wpdb; $order_id_val = $item->get_id(); $data['purchase_items'] = array_map( function ( $order_item ) use ( $item, $order_id_val, $wpdb ) { $oitem_id = (int) $order_item->get_id(); // Per-line shipping status (lookup) + saved courier/tracking (order // meta) so the admin order screen can prefill the fulfilment UI. // phpcs:disable WordPress.DB.DirectDatabaseQuery $shipping_status = (string) $wpdb->get_var( $wpdb->prepare( "SELECT shipping_status FROM {$wpdb->prefix}storeengine_order_product_lookup WHERE order_item_id = %d AND order_item_id > 0", $oitem_id ) ); // phpcs:enable $shipment = $item->get_meta( '_storeengine_shipment_' . $oitem_id ); return array_merge( [ 'id' => $oitem_id, 'edit_url' => get_post( $order_item->get_product_id() ) ? admin_url( 'admin.php?page=storeengine-products&id=' . $order_item->get_product_id() . '&action=edit' ) : '', ], $order_item->get_data(), [ 'formatted_metadata' => array_values( $order_item->get_all_formatted_metadata() ), 'order_id' => $order_id_val, 'order_item_id' => $oitem_id, 'shipping_type' => get_post_meta( $order_item->get_product_id(), '_storeengine_product_shipping_type', true ), 'shipping_status' => $shipping_status, 'shipment' => is_array( $shipment ) ? $shipment : null, ] ); }, $item->get_items() ); $data['purchase_items'] = array_values( $data['purchase_items'] ); } if ( isset( $schema['properties']['refunds'] ) ) { try { $data['refunds'] = array_map( fn( $refund ) => [ 'id' => $refund->get_id(), 'amount' => $refund->get_total(), 'reason' => $refund->get_reason(), 'refund_by' => [ 'user_id' => $refund->get_refunded_by() ? $refund->get_refunded_by_user()->get_id() : null, 'name' => $refund->get_refunded_by() ? trim( $refund->get_refunded_by_user()->get_first_name() . ' ' . $refund->get_refunded_by_user()->get_last_name() ) : null, 'display_name' => $refund->get_refunded_by() ? $refund->get_refunded_by_user()->get_display_name() : null, ], 'created_at' => $refund->get_date_created_gmt() ? $refund->get_date_created_gmt()->format( 'Y-m-d H:i:s' ) : null, ], $item->get_refunds() ); } catch ( StoreEngineException $e ) { $data['refunds'] = []; } } if ( isset( $schema['properties']['billing_address'] ) ) { $data['billing_address'] = $item->get_address(); if ( isset( $data['billing_address']['address_type'] ) ) { unset( $data['billing_address']['address_type'] ); } } if ( isset( $schema['properties']['shipping_address'] ) ) { $data['shipping_address'] = $item->get_address( 'shipping' ); if ( isset( $data['shipping_address']['address_type'] ) ) { unset( $data['shipping_address']['address_type'] ); } } $data['notes'] = $item->get_order_notes(); // Wrap the data in a response object. $response = rest_ensure_response( $data ); $response->add_links( $this->prepare_links( $item, $request ) ); return $response; } /** * @param WP_REST_Request $request * * @return WP_REST_Response|WP_Error */ public function create_item( $request ) { do_action( 'storeengine/api/before_create_order', $request ); try { $order = new StoreEngineOrder(); $order->set_status( OrderStatus::AUTO_DRAFT ); $order->set_created_via( 'admin-api' ); $order->save(); $order->read( true ); $response = $this->prepare_item_for_response( $order, $request ); return rest_ensure_response( $response ); } catch ( StoreEngineException $exception ) { return rest_ensure_response( $exception->toWpError() ); } catch ( Exception $exception ) { return rest_ensure_response( new WP_Error( 'something-went-wrong', $exception->getMessage(), [ 'status' => 500 ] ) ); } // @TODO create api endpoint with the ability to add order item // directly so we can trigger 'storeengine/api/after_create_order' hook for the lookup. // phpcs:disable Squiz.PHP.CommentedOutCode.Found // if ( ! isset( $request['purchase_items'] ) || empty( $request['purchase_items'] ) ) { // return new WP_Error( 'purchase_items_required', __( 'Please select at least one product!', 'storeengine' ) ); // } // // $order = new StoreEngineOrder(); // $customer_id = $request->get_param( 'customer_id' ); // $order->set_customer_id( $customer_id ); // // $order_email = $request['billing_email'] ?? ''; // if ( ! $order_email && ! $order->get_order_email( 'edit' ) && $order->get_customer_id( 'edit' ) ) { // $order_email = $order->get_customer()->get_email( 'edit' ); // } // // if ( $order_email && $order_email !== $order->get_order_email( 'edit' ) ) { // $order->set_order_email( $order_email ); // } // // $payment_gateway = null; // if ( isset( $request['payment_method'] ) ) { // $payment_gateway = Helper::get_payment_gateway( $request['payment_method'] ); // // if ( ! $payment_gateway ) { // return rest_ensure_response( new WP_Error( 'payment_gateway_error', __( 'Payment method not found.', 'storeengine' ), [ 'status' => 400 ] ) ); // } // } // // $order->set_status( $request['status'] ?? Constants::ORDER_STATUS_PENDING_PAYMENT ); // $order->set_order_email( $order_email ); // $order->set_payment_method( $payment_gateway ); // $order->set_customer_note( $request['order_note'] ?? '' ); // $order->set_transaction_id( $request['transaction_id'] ?? '' ); // // if ( ! empty( $request['billing_address'] ) && is_array( $request['billing_address'] ) ) { // $order->set_billing_address( $request['billing_address'] ); // } // // if ( ! empty( $request['shipping_address'] ) && is_array( $request['shipping_address'] ) ) { // $order->set_shipping_address( $request['shipping_address'] ); // } // // foreach ( $request['purchase_items'] as $purchase_item ) { // $order->add_product( $purchase_item['price_id'], $purchase_item['quantity'], array_merge( $purchase_item, [ 'order' => $order ] ) ); // } // // if ( ! empty( $request['coupons'] ) && is_array( $request['coupons'] ) ) { // foreach ( $request['coupons'] as $coupon ) { // if ( StringUtil::is_null_or_whitespace( $coupon ) ) { // continue; // } // $coupon = new Coupon( $coupon ); // $is_valid = $coupon->validate_coupon(); // if ( $is_valid ) { // $order->apply_coupon( $coupon ); // } // } // } // // $order->save(); // $order->recalculate_coupons(); // $order->calculate(); // $order->save(); // // do_action( 'storeengine/api/after_create_order', $order ); // // return rest_ensure_response( $this->prepare_item_for_response( $order, $request ) ); } public function get_item( $request ) { try { $order = Helper::get_order( $request['id'] ); if ( is_wp_error( $order ) ) { return $order; } $response = $this->prepare_item_for_response( $order, $request ); return rest_ensure_response( $response ); } catch ( StoreEngineException $exception ) { return rest_ensure_response( $exception->toWpError() ); } catch ( Exception $exception ) { return rest_ensure_response( new WP_Error( 'something-went-wrong', $exception->getMessage(), [ 'status' => 500 ] ) ); } } /** * @param WP_REST_Request $request * * @return WP_REST_Response|WP_Error * @throws StoreEngineException */ public function update_item( $request ) { do_action( 'storeengine/api/before_update_order', $request ); $order_id = $request->get_param( 'id' ); $order = Helper::get_order( $order_id ); if ( is_wp_error( $order ) ) { return $order; } if ( ! $order ) { return new WP_Error( 'invalid_order_id', __( 'Invalid order id!', 'storeengine' ) ); } // Status can be updated even if order is no longer editable. $previous_status = $order->get_status( 'edit' ); $body = $request->get_json_params(); $order = $this->set_core_order( $order, $body ); $order_email = $request['billing_email'] ?? ( $body['billing_email'] ?? '' ); if ( ! $order_email && ! $order->get_order_email( 'edit' ) && $order->get_customer_id( 'edit' ) ) { $order_email = $order->get_customer()->get_email( 'edit' ); } if ( $order_email && $order_email !== $order->get_order_email( 'edit' ) ) { $order->set_order_email( $order_email ); } if ( array_key_exists( 'billing_address', $body ) ) { $order = $this->set_order_address( $order, $body['billing_address'] ); } if ( array_key_exists( 'shipping_address', $body ) ) { $order = $this->set_order_address( $order, $body['shipping_address'], 'shipping' ); } $order->save(); $order->recalculate_coupons(); $order->calculate(); $order->save(); $response = $this->prepare_item_for_response( $order, $request ); if ( 'auto-draft' === $previous_status && $previous_status !== $order->get_status( 'edit' ) ) { do_action( 'storeengine/api/after_create_order', $order ); } else { do_action( 'storeengine/api/after_update_order', $order ); } return new WP_REST_Response( $response, 200 ); } /** * @param StoreEngineOrder $order Order Object. * @param array $data Data. * * @return StoreEngineOrder * @throws StoreEngineException */ protected function set_core_order( StoreEngineOrder $order, array $data ): StoreEngineOrder { if ( array_key_exists( 'billing_email', $data ) ) { $order->set_order_email( $data['billing_email'] ); } if ( array_key_exists( 'currency', $data ) ) { $order->set_currency( $data['currency'] ); } if ( array_key_exists( 'customer_id', $data ) ) { $order->set_customer_id( $data['customer_id'] ); } if ( array_key_exists( 'payment_method', $data ) ) { $order->set_payment_method( $data['payment_method'] ); } if ( array_key_exists( 'payment_method_title', $data ) ) { $order->set_payment_method_title( $data['payment_method_title'] ); } if ( array_key_exists( 'status', $data ) ) { $order->set_status( $data['status'] ); } if ( array_key_exists( 'transaction_id', $data ) ) { $order->set_transaction_id( (string) $data['transaction_id'] ); } return $order; } /** * @param StoreEngineOrder $order Order Object. * @param array $address Address Data. * @param string $type Address Type. * * @return StoreEngineOrder */ protected function set_order_address( StoreEngineOrder $order, array $address, string $type = 'billing' ): StoreEngineOrder { $fields = array( 'first_name', 'last_name', 'email', 'phone', 'address_1', 'address_2', 'state', 'city', 'country', 'postcode', ); foreach ( $fields as $field ) { if ( array_key_exists( $field, $address ) ) { $order->{"set_{$type}_{$field}"}( $address[ $field ] ?? '' ); } } return $order; } public function delete_item( $request ) { $force = (bool) $request['force']; $order_id = (int) $request['id']; $object = Helper::get_order( $order_id ); if ( is_wp_error( $object ) ) { return $object; } if ( ! $object || ! $object->get_id() ) { return new WP_Error( 'invalid_item_id', __( 'Invalid ID!', 'storeengine' ), [ 'status' => 404 ] ); } $result = false; $supports_trash = EMPTY_TRASH_DAYS > 0 && is_callable( [ $object, 'get_status' ] ) && $object->is_trashable(); /** * Filter whether an object is trashable. * * Return false to disable trash support for the object. * * @param boolean $supports_trash Whether the object type support trashing. * @param AbstractEntity $object The object being considered for trashing support. */ $supports_trash = apply_filters( "storeengine/api/{$object->get_object_type()}/object_trashable", $supports_trash, $object ); // Maybe check additional permissions. do_action( 'storeengine/api/before_delete_order', $request ); $request->set_param( 'context', 'edit' ); $response = $this->prepare_item_for_response( $object, $request ); if ( $force ) { $object->delete( true ); $result = 0 === $object->get_id(); } else { // If we don't support trashing for this type, error out. if ( ! $supports_trash ) { /* translators: %s: post type */ return new WP_Error( 'storeengine_rest_trash_not_supported', sprintf( __( 'The %s does not support trashing.', 'storeengine' ), $object->get_object_type() ), [ 'status' => 501 ] ); } // Otherwise, only trash if we haven't already. if ( is_callable( [ $object, 'get_status' ] ) ) { if ( 'trash' === $object->get_status() ) { /* translators: %s: post type */ return new WP_Error( 'storeengine_rest_already_trashed', sprintf( __( 'The %s has already been trashed.', 'storeengine' ), $object->get_object_type() ), [ 'status' => 410 ] ); } $object->trash(); $result = 'trash' === $object->get_status(); } } if ( ! $result ) { /* translators: %s: post type */ return new WP_Error( 'storeengine_rest_cannot_delete', sprintf( __( 'Failed to delete %s.', 'storeengine' ), $object->get_object_type() ), [ 'status' => 500 ] ); } /** * Fires after a single object is deleted or trashed via the REST API. * * @param AbstractEntity $object The deleted or trashed object. * @param WP_REST_Response $response The response data. * @param WP_REST_Request $request The request sent to the API. */ do_action( "storeengine/api/{$object->get_object_type()}delete__object", $object, $response, $request ); do_action( 'storeengine/api/after_delete_order', $object, $order_id, $force ); return rest_ensure_response( [ 'previous' => $response, 'id' => $order_id, 'force' => $force, ] ); } public function get_states( WP_REST_Request $request ): WP_REST_Response { $cc = strtoupper( $request->get_param( 'cc' ) ); $states = Countries::init()->get_states( $cc ); return new WP_REST_Response( [ 'cc' => $cc, 'states' => $states ? $states : false, ], 200 ); } public function permissions_check() { return Helper::check_rest_user_cap( 'manage_options' ); } /** * @param $cart * * @return array * * @deprecated */ protected function prepare_purchase_items_data( $cart ): array { $purchase_items = []; foreach ( $cart->get_cart_items() as $product ) { $purchase_items[] = [ 'product_id' => $product['id'], 'variation_id' => 0, 'price_id' => $product['price_id'], 'product_qty' => $product['quantity'], 'price' => $product['price'], 'coupon_amount' => 0, 'tax_amount' => 0, 'shipping_amount' => 0, 'shipping_tax_amount' => 0, ]; } return $purchase_items; } /** * @param $request * * @return array * * @deprecated */ protected function prepare_item_for_database( $request ): array { // Insert purchase items into cart with for each loop. $request['customer_id'] = empty( $request['customer_id'] ) ? $request['id'] : $request['customer_id']; $cart_model = new \StoreEngine\Models\Cart( $request['customer_id'] ); $purchase_items = $this->prepare_purchase_items_data( $cart_model ); return [ 'status' => OrderStatus::PAYMENT_PENDING, 'currency' => Formatting::get_currency(), 'type' => 'onetime', 'customer_id' => $request['customer_id'], 'billing_email' => $request['billing_email'], 'payment_method' => $request['payment_method'] ?? '', 'payment_method_title' => $request['payment_method'] ?? '', 'customer_note' => $request['order_note'] ?? '', 'transaction_id' => $request['transaction_id'] ?? '', 'meta' => [], 'purchase_items' => $purchase_items, 'billing_address' => [ 'first_name' => $request['billing_address']['first_name'] ?? '', 'last_name' => $request['billing_address']['last_name'] ?? '', 'company' => $request['billing_address']['company'] ?? '', 'address_1' => $request['billing_address']['address_1'] ?? '', 'address_2' => $request['billing_address']['address_2'] ?? '', 'city' => $request['billing_address']['city'] ?? '', 'state' => $request['billing_address']['state'] ?? '', 'postcode' => $request['billing_address']['postcode'] ?? '', 'country' => $request['billing_address']['country'] ?? '', 'email' => $request['billing_address']['email'] ?? '', 'phone' => $request['billing_address']['phone'] ?? '', ], 'shipping_address' => [ 'first_name' => $request['shipping_address']['first_name'] ?? '', 'last_name' => $request['shipping_address']['last_name'] ?? '', 'company' => $request['shipping_address']['company'] ?? '', 'address_1' => $request['shipping_address']['address_1'] ?? '', 'address_2' => $request['shipping_address']['address_2'] ?? '', 'city' => $request['shipping_address']['city'] ?? '', 'state' => $request['shipping_address']['state'] ?? '', 'postcode' => $request['shipping_address']['postcode'] ?? '', 'country' => $request['shipping_address']['country'] ?? '', 'email' => $request['shipping_address']['email'] ?? '', 'phone' => $request['shipping_address']['phone'] ?? '', ], ]; } /** * @param array $schema * @param $order_purchase_items * * @return array * @deprecated */ protected function _prepare_order_purchase_items_for_database( array $schema, $order_purchase_items ): array { $order_details = []; $keys = [ 'product_id' => 'int', 'variation_id' => 'int', 'product_qty' => 'int', 'coupon_amount' => 'float', 'tax_amount' => 'float', 'shipping_amount' => 'float', 'shipping_tax_amount' => 'float', ]; foreach ( $order_purchase_items as $key => $order_item ) { foreach ( $keys as $field => $type ) { if ( ! empty( $schema['purchase_items']['properties'][ $field ] ) && isset( $order_item[ $field ] ) ) { settype( $order_item[ $field ], $type ); $order_details[ $key ][ $field ] = $order_item[ $field ]; } } } return $order_details; } /** * @param array $schema * @param $request_billing_address * * @return array * @deprecated */ protected function _prepare_order_billing_address_for_database( array $schema, $request_billing_address ): array { $billing_address = []; $keys = [ 'first_name' => 'string', 'last_name' => 'string', 'company' => 'string', 'address_1' => 'string', 'address_2' => 'string', 'city' => 'string', 'state' => 'string', 'postcode' => 'string', 'country' => 'string', 'email' => 'string', 'phone' => 'string', ]; foreach ( $keys as $key => $type ) { if ( ! empty( $schema['billing_address']['properties'][ $key ] ) && isset( $request_billing_address[ $key ] ) ) { $value = $request_billing_address[ $key ]; settype( $value, $type ); $billing_address[ $key ] = $value; } } return $billing_address; } }