| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530 |
- <?php
- /**
- * REST API Order Refunds controller
- *
- * Handles requests to the /orders/<order_id>/refunds endpoint.
- *
- * @author WooThemes
- * @category API
- * @package WooCommerce/API
- * @since 2.6.0
- */
- if ( ! defined( 'ABSPATH' ) ) {
- exit;
- }
- /**
- * REST API Order Refunds controller class.
- *
- * @package WooCommerce/API
- * @extends WC_REST_Orders_V1_Controller
- */
- class WC_REST_Order_Refunds_V1_Controller extends WC_REST_Orders_V1_Controller {
- /**
- * Endpoint namespace.
- *
- * @var string
- */
- protected $namespace = 'wc/v1';
- /**
- * Route base.
- *
- * @var string
- */
- protected $rest_base = 'orders/(?P<order_id>[\d]+)/refunds';
- /**
- * Post type.
- *
- * @var string
- */
- protected $post_type = 'shop_order_refund';
- /**
- * Order refunds actions.
- */
- public function __construct() {
- add_filter( "woocommerce_rest_{$this->post_type}_trashable", '__return_false' );
- add_filter( "woocommerce_rest_{$this->post_type}_query", array( $this, 'query_args' ), 10, 2 );
- }
- /**
- * Register the routes for order refunds.
- */
- public function register_routes() {
- register_rest_route( $this->namespace, '/' . $this->rest_base, array(
- 'args' => array(
- 'order_id' => array(
- 'description' => __( 'The order ID.', 'woocommerce' ),
- 'type' => 'integer',
- ),
- ),
- array(
- 'methods' => WP_REST_Server::READABLE,
- 'callback' => array( $this, 'get_items' ),
- 'permission_callback' => array( $this, 'get_items_permissions_check' ),
- 'args' => $this->get_collection_params(),
- ),
- array(
- 'methods' => WP_REST_Server::CREATABLE,
- 'callback' => array( $this, 'create_item' ),
- 'permission_callback' => array( $this, 'create_item_permissions_check' ),
- 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::CREATABLE ),
- ),
- 'schema' => array( $this, 'get_public_item_schema' ),
- ) );
- register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<id>[\d]+)', array(
- 'args' => array(
- 'order_id' => array(
- 'description' => __( 'The order ID.', 'woocommerce' ),
- 'type' => 'integer',
- ),
- 'id' => array(
- 'description' => __( 'Unique identifier for the resource.', 'woocommerce' ),
- 'type' => 'integer',
- ),
- ),
- array(
- 'methods' => WP_REST_Server::READABLE,
- 'callback' => array( $this, 'get_item' ),
- 'permission_callback' => array( $this, 'get_item_permissions_check' ),
- 'args' => array(
- 'context' => $this->get_context_param( array( 'default' => 'view' ) ),
- ),
- ),
- array(
- 'methods' => WP_REST_Server::DELETABLE,
- 'callback' => array( $this, 'delete_item' ),
- 'permission_callback' => array( $this, 'delete_item_permissions_check' ),
- 'args' => array(
- 'force' => array(
- 'default' => true,
- 'type' => 'boolean',
- 'description' => __( 'Required to be true, as resource does not support trashing.', 'woocommerce' ),
- ),
- ),
- ),
- 'schema' => array( $this, 'get_public_item_schema' ),
- ) );
- }
- /**
- * Prepare a single order refund output for response.
- *
- * @param WP_Post $post Post object.
- * @param WP_REST_Request $request Request object.
- *
- * @return WP_Error|WP_REST_Response
- */
- public function prepare_item_for_response( $post, $request ) {
- $order = wc_get_order( (int) $request['order_id'] );
- if ( ! $order ) {
- return new WP_Error( 'woocommerce_rest_invalid_order_id', __( 'Invalid order ID.', 'woocommerce' ), 404 );
- }
- $refund = wc_get_order( $post );
- if ( ! $refund || $refund->get_parent_id() !== $order->get_id() ) {
- return new WP_Error( 'woocommerce_rest_invalid_order_refund_id', __( 'Invalid order refund ID.', 'woocommerce' ), 404 );
- }
- $dp = is_null( $request['dp'] ) ? wc_get_price_decimals() : absint( $request['dp'] );
- $data = array(
- 'id' => $refund->get_id(),
- 'date_created' => wc_rest_prepare_date_response( $refund->get_date_created() ),
- 'amount' => wc_format_decimal( $refund->get_amount(), $dp ),
- 'reason' => $refund->get_reason(),
- 'line_items' => array(),
- );
- // Add line items.
- foreach ( $refund->get_items() as $item_id => $item ) {
- $product = $refund->get_product_from_item( $item );
- $product_id = 0;
- $variation_id = 0;
- $product_sku = null;
- // Check if the product exists.
- if ( is_object( $product ) ) {
- $product_id = $item->get_product_id();
- $variation_id = $item->get_variation_id();
- $product_sku = $product->get_sku();
- }
- $item_meta = array();
- $hideprefix = 'true' === $request['all_item_meta'] ? null : '_';
- foreach ( $item->get_formatted_meta_data( $hideprefix, true ) as $meta_key => $formatted_meta ) {
- $item_meta[] = array(
- 'key' => $formatted_meta->key,
- 'label' => $formatted_meta->display_key,
- 'value' => wc_clean( $formatted_meta->display_value ),
- );
- }
- $line_item = array(
- 'id' => $item_id,
- 'name' => $item['name'],
- 'sku' => $product_sku,
- 'product_id' => (int) $product_id,
- 'variation_id' => (int) $variation_id,
- 'quantity' => wc_stock_amount( $item['qty'] ),
- 'tax_class' => ! empty( $item['tax_class'] ) ? $item['tax_class'] : '',
- 'price' => wc_format_decimal( $refund->get_item_total( $item, false, false ), $dp ),
- 'subtotal' => wc_format_decimal( $refund->get_line_subtotal( $item, false, false ), $dp ),
- 'subtotal_tax' => wc_format_decimal( $item['line_subtotal_tax'], $dp ),
- 'total' => wc_format_decimal( $refund->get_line_total( $item, false, false ), $dp ),
- 'total_tax' => wc_format_decimal( $item['line_tax'], $dp ),
- 'taxes' => array(),
- 'meta' => $item_meta,
- );
- $item_line_taxes = maybe_unserialize( $item['line_tax_data'] );
- if ( isset( $item_line_taxes['total'] ) ) {
- $line_tax = array();
- foreach ( $item_line_taxes['total'] as $tax_rate_id => $tax ) {
- $line_tax[ $tax_rate_id ] = array(
- 'id' => $tax_rate_id,
- 'total' => $tax,
- 'subtotal' => '',
- );
- }
- foreach ( $item_line_taxes['subtotal'] as $tax_rate_id => $tax ) {
- $line_tax[ $tax_rate_id ]['subtotal'] = $tax;
- }
- $line_item['taxes'] = array_values( $line_tax );
- }
- $data['line_items'][] = $line_item;
- }
- $context = ! empty( $request['context'] ) ? $request['context'] : 'view';
- $data = $this->add_additional_fields_to_object( $data, $request );
- $data = $this->filter_response_by_context( $data, $context );
- // Wrap the data in a response object.
- $response = rest_ensure_response( $data );
- $response->add_links( $this->prepare_links( $refund, $request ) );
- /**
- * Filter the data for a response.
- *
- * The dynamic portion of the hook name, $this->post_type, refers to post_type of the post being
- * prepared for the response.
- *
- * @param WP_REST_Response $response The response object.
- * @param WP_Post $post Post object.
- * @param WP_REST_Request $request Request object.
- */
- return apply_filters( "woocommerce_rest_prepare_{$this->post_type}", $response, $post, $request );
- }
- /**
- * Prepare links for the request.
- *
- * @param WC_Order_Refund $refund Comment object.
- * @param WP_REST_Request $request Request object.
- * @return array Links for the given order refund.
- */
- protected function prepare_links( $refund, $request ) {
- $order_id = $refund->get_parent_id();
- $base = str_replace( '(?P<order_id>[\d]+)', $order_id, $this->rest_base );
- $links = array(
- 'self' => array(
- 'href' => rest_url( sprintf( '/%s/%s/%d', $this->namespace, $base, $refund->get_id() ) ),
- ),
- 'collection' => array(
- 'href' => rest_url( sprintf( '/%s/%s', $this->namespace, $base ) ),
- ),
- 'up' => array(
- 'href' => rest_url( sprintf( '/%s/orders/%d', $this->namespace, $order_id ) ),
- ),
- );
- return $links;
- }
- /**
- * Query args.
- *
- * @param array $args Request args.
- * @param WP_REST_Request $request Request object.
- * @return array
- */
- public function query_args( $args, $request ) {
- $args['post_status'] = array_keys( wc_get_order_statuses() );
- $args['post_parent__in'] = array( absint( $request['order_id'] ) );
- return $args;
- }
- /**
- * Create a single item.
- *
- * @param WP_REST_Request $request Full details about the request.
- * @return WP_Error|WP_REST_Response
- */
- public function create_item( $request ) {
- if ( ! empty( $request['id'] ) ) {
- /* translators: %s: post type */
- return new WP_Error( "woocommerce_rest_{$this->post_type}_exists", sprintf( __( 'Cannot create existing %s.', 'woocommerce' ), $this->post_type ), array( 'status' => 400 ) );
- }
- $order_data = get_post( (int) $request['order_id'] );
- if ( empty( $order_data ) ) {
- return new WP_Error( 'woocommerce_rest_invalid_order', __( 'Order is invalid', 'woocommerce' ), 400 );
- }
- if ( 0 > $request['amount'] ) {
- return new WP_Error( 'woocommerce_rest_invalid_order_refund', __( 'Refund amount must be greater than zero.', 'woocommerce' ), 400 );
- }
- // Create the refund.
- $refund = wc_create_refund( array(
- 'order_id' => $order_data->ID,
- 'amount' => $request['amount'],
- 'reason' => empty( $request['reason'] ) ? null : $request['reason'],
- 'refund_payment' => is_bool( $request['api_refund'] ) ? $request['api_refund'] : true,
- 'restock_items' => true,
- ) );
- if ( is_wp_error( $refund ) ) {
- return new WP_Error( 'woocommerce_rest_cannot_create_order_refund', $refund->get_error_message(), 500 );
- }
- if ( ! $refund ) {
- return new WP_Error( 'woocommerce_rest_cannot_create_order_refund', __( 'Cannot create order refund, please try again.', 'woocommerce' ), 500 );
- }
- $post = get_post( $refund->get_id() );
- $this->update_additional_fields_for_object( $post, $request );
- /**
- * Fires after a single item is created or updated via the REST API.
- *
- * @param WP_Post $post Post object.
- * @param WP_REST_Request $request Request object.
- * @param boolean $creating True when creating item, false when updating.
- */
- do_action( "woocommerce_rest_insert_{$this->post_type}", $post, $request, true );
- $request->set_param( 'context', 'edit' );
- $response = $this->prepare_item_for_response( $post, $request );
- $response = rest_ensure_response( $response );
- $response->set_status( 201 );
- $response->header( 'Location', rest_url( sprintf( '/%s/%s/%d', $this->namespace, $this->rest_base, $post->ID ) ) );
- return $response;
- }
- /**
- * Get the Order's schema, conforming to JSON Schema.
- *
- * @return array
- */
- public function get_item_schema() {
- $schema = array(
- '$schema' => 'http://json-schema.org/draft-04/schema#',
- 'title' => $this->post_type,
- 'type' => 'object',
- 'properties' => array(
- 'id' => array(
- 'description' => __( 'Unique identifier for the resource.', 'woocommerce' ),
- 'type' => 'integer',
- 'context' => array( 'view', 'edit' ),
- 'readonly' => true,
- ),
- 'date_created' => array(
- 'description' => __( "The date the order refund was created, in the site's timezone.", 'woocommerce' ),
- 'type' => 'date-time',
- 'context' => array( 'view', 'edit' ),
- 'readonly' => true,
- ),
- 'amount' => array(
- 'description' => __( 'Refund amount.', 'woocommerce' ),
- 'type' => 'string',
- 'context' => array( 'view', 'edit' ),
- ),
- 'reason' => array(
- 'description' => __( 'Reason for refund.', 'woocommerce' ),
- 'type' => 'string',
- 'context' => array( 'view', 'edit' ),
- ),
- 'line_items' => array(
- 'description' => __( 'Line items data.', 'woocommerce' ),
- 'type' => 'array',
- 'context' => array( 'view', 'edit' ),
- 'readonly' => true,
- 'items' => array(
- 'type' => 'object',
- 'properties' => array(
- 'id' => array(
- 'description' => __( 'Item ID.', 'woocommerce' ),
- 'type' => 'integer',
- 'context' => array( 'view', 'edit' ),
- 'readonly' => true,
- ),
- 'name' => array(
- 'description' => __( 'Product name.', 'woocommerce' ),
- 'type' => 'mixed',
- 'context' => array( 'view', 'edit' ),
- 'readonly' => true,
- ),
- 'sku' => array(
- 'description' => __( 'Product SKU.', 'woocommerce' ),
- 'type' => 'string',
- 'context' => array( 'view', 'edit' ),
- 'readonly' => true,
- ),
- 'product_id' => array(
- 'description' => __( 'Product ID.', 'woocommerce' ),
- 'type' => 'mixed',
- 'context' => array( 'view', 'edit' ),
- 'readonly' => true,
- ),
- 'variation_id' => array(
- 'description' => __( 'Variation ID, if applicable.', 'woocommerce' ),
- 'type' => 'integer',
- 'context' => array( 'view', 'edit' ),
- 'readonly' => true,
- ),
- 'quantity' => array(
- 'description' => __( 'Quantity ordered.', 'woocommerce' ),
- 'type' => 'integer',
- 'context' => array( 'view', 'edit' ),
- 'readonly' => true,
- ),
- 'tax_class' => array(
- 'description' => __( 'Tax class of product.', 'woocommerce' ),
- 'type' => 'string',
- 'context' => array( 'view', 'edit' ),
- 'readonly' => true,
- ),
- 'price' => array(
- 'description' => __( 'Product price.', 'woocommerce' ),
- 'type' => 'string',
- 'context' => array( 'view', 'edit' ),
- 'readonly' => true,
- ),
- 'subtotal' => array(
- 'description' => __( 'Line subtotal (before discounts).', 'woocommerce' ),
- 'type' => 'string',
- 'context' => array( 'view', 'edit' ),
- 'readonly' => true,
- ),
- 'subtotal_tax' => array(
- 'description' => __( 'Line subtotal tax (before discounts).', 'woocommerce' ),
- 'type' => 'string',
- 'context' => array( 'view', 'edit' ),
- 'readonly' => true,
- ),
- 'total' => array(
- 'description' => __( 'Line total (after discounts).', 'woocommerce' ),
- 'type' => 'string',
- 'context' => array( 'view', 'edit' ),
- 'readonly' => true,
- ),
- 'total_tax' => array(
- 'description' => __( 'Line total tax (after discounts).', 'woocommerce' ),
- 'type' => 'string',
- 'context' => array( 'view', 'edit' ),
- 'readonly' => true,
- ),
- 'taxes' => array(
- 'description' => __( 'Line taxes.', 'woocommerce' ),
- 'type' => 'array',
- 'context' => array( 'view', 'edit' ),
- 'readonly' => true,
- 'items' => array(
- 'type' => 'object',
- 'properties' => array(
- 'id' => array(
- 'description' => __( 'Tax rate ID.', 'woocommerce' ),
- 'type' => 'integer',
- 'context' => array( 'view', 'edit' ),
- 'readonly' => true,
- ),
- 'total' => array(
- 'description' => __( 'Tax total.', 'woocommerce' ),
- 'type' => 'string',
- 'context' => array( 'view', 'edit' ),
- 'readonly' => true,
- ),
- 'subtotal' => array(
- 'description' => __( 'Tax subtotal.', 'woocommerce' ),
- 'type' => 'string',
- 'context' => array( 'view', 'edit' ),
- 'readonly' => true,
- ),
- ),
- ),
- ),
- 'meta' => array(
- 'description' => __( 'Line item meta data.', 'woocommerce' ),
- 'type' => 'array',
- 'context' => array( 'view', 'edit' ),
- 'readonly' => true,
- 'items' => array(
- 'type' => 'object',
- 'properties' => array(
- 'key' => array(
- 'description' => __( 'Meta key.', 'woocommerce' ),
- 'type' => 'string',
- 'context' => array( 'view', 'edit' ),
- 'readonly' => true,
- ),
- 'label' => array(
- 'description' => __( 'Meta label.', 'woocommerce' ),
- 'type' => 'string',
- 'context' => array( 'view', 'edit' ),
- 'readonly' => true,
- ),
- 'value' => array(
- 'description' => __( 'Meta value.', 'woocommerce' ),
- 'type' => 'mixed',
- 'context' => array( 'view', 'edit' ),
- 'readonly' => true,
- ),
- ),
- ),
- ),
- ),
- ),
- ),
- ),
- );
- return $this->add_additional_fields_schema( $schema );
- }
- /**
- * Get the query params for collections.
- *
- * @return array
- */
- public function get_collection_params() {
- $params = parent::get_collection_params();
- $params['dp'] = array(
- 'default' => wc_get_price_decimals(),
- 'description' => __( 'Number of decimal points to use in each resource.', 'woocommerce' ),
- 'type' => 'integer',
- 'sanitize_callback' => 'absint',
- 'validate_callback' => 'rest_validate_request_arg',
- );
- return $params;
- }
- }
|