Completed
Pull Request — master (#11716)
by Mike
10:27
created

WC_REST_Orders_Controller::prepare_fee_lines()   C

Complexity

Conditions 8
Paths 33

Size

Total Lines 27
Code Lines 14

Duplication

Lines 5
Ratio 18.52 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 8
eloc 14
c 1
b 0
f 0
nc 33
nop 2
dl 5
loc 27
rs 5.3846
1
<?php
2
/**
3
 * REST API Orders controller
4
 *
5
 * Handles requests to the /orders endpoint.
6
 *
7
 * @author   WooThemes
8
 * @category API
9
 * @package  WooCommerce/API
10
 * @since    2.6.0
11
 */
12
13
if ( ! defined( 'ABSPATH' ) ) {
14
	exit;
15
}
16
17
/**
18
 * REST API Orders controller class.
19
 *
20
 * @package WooCommerce/API
21
 * @extends WC_REST_Posts_Controller
22
 */
23
class WC_REST_Orders_Controller extends WC_REST_Posts_Controller {
24
25
	/**
26
	 * Endpoint namespace.
27
	 *
28
	 * @var string
29
	 */
30
	protected $namespace = 'wc/v1';
31
32
	/**
33
	 * Route base.
34
	 *
35
	 * @var string
36
	 */
37
	protected $rest_base = 'orders';
38
39
	/**
40
	 * Post type.
41
	 *
42
	 * @var string
43
	 */
44
	protected $post_type = 'shop_order';
45
46
	/**
47
	 * DP for rounding.
48
	 *
49
	 * @var int
50
	 */
51
	protected $dp = '2';
52
53
	/**
54
	 * Stores the request.
55
	 * @var array
56
	 */
57
	protected $request = array();
58
59
	/**
60
	 * Initialize orders actions.
61
	 */
62
	public function __construct() {
63
		add_filter( "woocommerce_rest_{$this->post_type}_query", array( $this, 'query_args' ), 10, 2 );
64
	}
65
66
	/**
67
	 * Register the routes for orders.
68
	 */
69
	public function register_routes() {
70
		register_rest_route( $this->namespace, '/' . $this->rest_base, array(
71
			array(
72
				'methods'             => WP_REST_Server::READABLE,
73
				'callback'            => array( $this, 'get_items' ),
74
				'permission_callback' => array( $this, 'get_items_permissions_check' ),
75
				'args'                => $this->get_collection_params(),
76
			),
77
			array(
78
				'methods'             => WP_REST_Server::CREATABLE,
79
				'callback'            => array( $this, 'create_item' ),
80
				'permission_callback' => array( $this, 'create_item_permissions_check' ),
81
				'args'                => $this->get_endpoint_args_for_item_schema( WP_REST_Server::CREATABLE ),
82
			),
83
			'schema' => array( $this, 'get_public_item_schema' ),
84
		) );
85
86
		register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<id>[\d]+)', array(
87
			array(
88
				'methods'             => WP_REST_Server::READABLE,
89
				'callback'            => array( $this, 'get_item' ),
90
				'permission_callback' => array( $this, 'get_item_permissions_check' ),
91
				'args'                => array(
92
					'context' => $this->get_context_param( array( 'default' => 'view' ) ),
93
				),
94
			),
95
			array(
96
				'methods'             => WP_REST_Server::EDITABLE,
97
				'callback'            => array( $this, 'update_item' ),
98
				'permission_callback' => array( $this, 'update_item_permissions_check' ),
99
				'args'                => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
100
			),
101
			array(
102
				'methods'             => WP_REST_Server::DELETABLE,
103
				'callback'            => array( $this, 'delete_item' ),
104
				'permission_callback' => array( $this, 'delete_item_permissions_check' ),
105
				'args'                => array(
106
					'force' => array(
107
						'default'     => false,
108
						'description' => __( 'Whether to bypass trash and force deletion.', 'woocommerce' ),
109
					),
110
					'reassign' => array(),
111
				),
112
			),
113
			'schema' => array( $this, 'get_public_item_schema' ),
114
		) );
115
116
		register_rest_route( $this->namespace, '/' . $this->rest_base . '/batch', array(
117
			array(
118
				'methods'             => WP_REST_Server::EDITABLE,
119
				'callback'            => array( $this, 'batch_items' ),
120
				'permission_callback' => array( $this, 'batch_items_permissions_check' ),
121
				'args'                => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
122
			),
123
			'schema' => array( $this, 'get_public_batch_schema' ),
124
		) );
125
	}
126
127
	/**
128
	 * Expands an order item to get its data.
129
	 * @param WC_Order_item $item
130
	 * @return array
131
	 */
132
	protected function get_order_item_data( $item ) {
133
		$data           = $item->get_data();
134
		$format_decimal = array( 'subtotal', 'subtotal_tax', 'total', 'total_tax', 'tax_total', 'shipping_tax_total' );
135
136
		// Format decimal values.
137 View Code Duplication
		foreach ( $format_decimal as $key ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
138
			if ( isset( $data[ $key ] ) ) {
139
				$data[ $key ] = wc_format_decimal( $data[ $key ], $this->request['dp'] );
140
			}
141
		}
142
143
		// Add meta, SKU and PRICE to products.
144
		if ( is_callable( array( $item, 'get_product' ) ) ) {
145
			$data['sku']   = $item->get_product() ? $item->get_product()->get_sku(): null;
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class WC_Order_Item as the method get_product() does only exist in the following sub-classes of WC_Order_Item: WC_Order_Item_Product. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
146
			$data['price'] = $item->get_total() / max( 1, $item->get_quantity() );
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class WC_Order_Item as the method get_total() does only exist in the following sub-classes of WC_Order_Item: WC_Order_Item_Fee, WC_Order_Item_Product, WC_Order_Item_Shipping. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
147
148
			// Format meta data.
149
			if ( isset( $data['meta_data'] ) ) {
150
				$hideprefix = 'true' === $this->request['all_item_meta'] ? null : '_';
151
				$item_meta  = $item->get_formatted_meta_data( $hideprefix );
152
153
				foreach ( $item_meta as $key => $values ) {
154
					// Label was used in previous version of API - set it here.
155
					$item_meta[ $key ]->label = $values->display_key;
156
					unset( $item_meta[ $key ]->display_key );
157
					unset( $item_meta[ $key ]->display_value );
158
				}
159
160
				$data['meta'] = array_values( $item_meta );
161
			}
162
		}
163
164
		// Format taxes.
165
		if ( ! empty( $data['taxes']['total'] ) ) {
166
			$taxes = array();
167
168
			foreach ( $data['taxes']['total'] as $tax_rate_id => $tax ) {
169
				$taxes[] = array(
170
					'id'       => $tax_rate_id,
171
					'total'    => $tax,
172
					'subtotal' => isset( $data['taxes']['subtotal'][ $tax_rate_id ] ) ? $data['taxes']['subtotal'][ $tax_rate_id ] : '',
173
				);
174
			}
175
			$data['taxes'] = $taxes;
176
		} elseif ( isset( $data['taxes'] ) ) {
177
			$data['taxes'] = array();
178
		}
179
180
		// Remove props we don't want to expose.
181
		unset( $data['order_id'] );
182
		unset( $data['type'] );
183
184
		return $data;
185
	}
186
187
	/**
188
	 * Prepare a single order output for response.
189
	 *
190
	 * @param WP_Post $post Post object.
191
	 * @param WP_REST_Request $request Request object.
192
	 * @return WP_REST_Response $data
193
	 */
194
	public function prepare_item_for_response( $post, $request ) {
195
		$this->request     = $request;
0 ignored issues
show
Documentation Bug introduced by
It seems like $request of type object<WP_REST_Request> is incompatible with the declared type array of property $request.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
196
		$statuses          = wc_get_order_statuses();
0 ignored issues
show
Unused Code introduced by
$statuses is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
197
		$order             = wc_get_order( $post );
198
		$data              = $order->get_data();
199
		$format_decimal    = array( 'discount_total', 'discount_tax', 'shipping_total', 'shipping_tax', 'shipping_total', 'shipping_tax', 'cart_tax', 'total', 'total_tax' );
200
		$format_date       = array( 'date_created', 'date_modified', 'date_completed' );
201
		$format_line_items = array( 'line_items', 'tax_lines', 'shipping_lines', 'fee_lines', 'coupon_lines' );
202
203
		// Format decimal values.
204 View Code Duplication
		foreach ( $format_decimal as $key ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
205
			$data[ $key ] = wc_format_decimal( $data[ $key ], $this->request['dp'] );
206
		}
207
208
		// Format date values.
209
		foreach ( $format_date as $key ) {
210
			$data[ $key ] = $data[ $key ] ? wc_rest_prepare_date_response( get_gmt_from_date( date( 'Y-m-d H:i:s', $data[ $key ] ) ) ) : false;
211
		}
212
213
		// Format the order status.
214
		$data['status'] = 'wc-' === substr( $data['status'], 0, 3 ) ? substr( $data['status'], 3 ) : $data['status'];
215
216
		// Format line items.
217
		foreach ( $format_line_items as $key ) {
218
			$data[ $key ] = array_values( array_map( array( $this, 'get_order_item_data' ), $data[ $key ] ) );
219
		}
220
221
		// Refunds.
222
		foreach ( $order->get_refunds() as $refund ) {
223
			$data['refunds'][] = array(
224
				'id'     => $refund->id,
225
				'refund' => $refund->get_refund_reason() ? $refund->get_refund_reason() : '',
226
				'total'  => '-' . wc_format_decimal( $refund->get_refund_amount(), $this->request['dp'] ),
227
			);
228
		}
229
230
		$context  = ! empty( $request['context'] ) ? $request['context'] : 'view';
231
		$data     = $this->add_additional_fields_to_object( $data, $request );
232
		$data     = $this->filter_response_by_context( $data, $context );
233
		$response = rest_ensure_response( $data );
234
		$response->add_links( $this->prepare_links( $order ) );
0 ignored issues
show
Documentation introduced by
$order is of type false|object, but the function expects a object<WC_Order>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
235
236
		/**
237
		 * Filter the data for a response.
238
		 *
239
		 * The dynamic portion of the hook name, $this->post_type, refers to post_type of the post being
240
		 * prepared for the response.
241
		 *
242
		 * @param WP_REST_Response   $response   The response object.
243
		 * @param WP_Post            $post       Post object.
244
		 * @param WP_REST_Request    $request    Request object.
245
		 */
246
		return apply_filters( "woocommerce_rest_prepare_{$this->post_type}", $response, $post, $request );
247
	}
248
249
	/**
250
	 * Prepare links for the request.
251
	 *
252
	 * @param WC_Order $order Order object.
253
	 * @return array Links for the given order.
254
	 */
255
	protected function prepare_links( $order ) {
256
		$links = array(
257
			'self' => array(
258
				'href' => rest_url( sprintf( '/%s/%s/%d', $this->namespace, $this->rest_base, $order->get_id() ) ),
259
			),
260
			'collection' => array(
261
				'href' => rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ),
262
			),
263
		);
264 View Code Duplication
		if ( 0 !== (int) $order->get_user_id() ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
265
			$links['customer'] = array(
266
				'href' => rest_url( sprintf( '/%s/customers/%d', $this->namespace, $order->get_user_id() ) ),
267
			);
268
		}
269 View Code Duplication
		if ( 0 !== (int) $order->get_parent_id() ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
270
			$links['up'] = array(
271
				'href' => rest_url( sprintf( '/%s/orders/%d', $this->namespace, $order->get_parent_id() ) ),
272
			);
273
		}
274
		return $links;
275
	}
276
277
	/**
278
	 * Query args.
279
	 *
280
	 * @param array $args
281
	 * @param WP_REST_Request $request
282
	 * @return array
283
	 */
284
	public function query_args( $args, $request ) {
285
		global $wpdb;
286
287
		// Set post_status.
288
		if ( 'any' !== $request['status'] ) {
289
			$args['post_status'] = 'wc-' . $request['status'];
290
		} else {
291
			$args['post_status'] = 'any';
292
		}
293
294 View Code Duplication
		if ( ! empty( $request['customer'] ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
295
			if ( ! empty( $args['meta_query'] ) ) {
296
				$args['meta_query'] = array();
297
			}
298
299
			$args['meta_query'][] = array(
300
				'key'   => '_customer_user',
301
				'value' => $request['customer'],
302
				'type'  => 'NUMERIC',
303
			);
304
		}
305
306
		// Search by product.
307
		if ( ! empty( $request['product'] ) ) {
308
			$order_ids = $wpdb->get_col( $wpdb->prepare( "
309
				SELECT order_id
310
				FROM {$wpdb->prefix}woocommerce_order_items
311
				WHERE order_item_id IN ( SELECT order_item_id FROM {$wpdb->prefix}woocommerce_order_itemmeta WHERE meta_key = '_product_id' AND meta_value = %d )
312
				AND order_item_type = 'line_item'
313
			 ", $request['product'] ) );
314
315
			// Force WP_Query return empty if don't found any order.
316
			$order_ids = ! empty( $order_ids ) ? $order_ids : array( 0 );
317
318
			$args['post__in'] = $order_ids;
319
		}
320
321
		// Search.
322
		if ( ! empty( $args['s'] ) ) {
323
			$order_ids = wc_order_search( $args['s'] );
0 ignored issues
show
Bug introduced by
It seems like $args['s'] can also be of type array; however, wc_order_search() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
324
325
			if ( ! empty( $order_ids ) ) {
326
				unset( $args['s'] );
327
				$args['post__in'] =  array_merge( $order_ids, array( 0 ) );
328
			}
329
		}
330
331
		return $args;
332
	}
333
334
	/**
335
	 * Prepare a single order for create.
336
	 *
337
	 * @param  WP_REST_Request $request Request object.
338
	 * @return WP_Error|WC_Order $data Object.
339
	 */
340
	protected function prepare_item_for_database( $request ) {
341
		$id        = isset( $request['id'] ) ? absint( $request['id'] ) : 0;
342
		$order     = new WC_Order( $id );
343
		$schema    = $this->get_item_schema();
344
		$data_keys = array_keys( array_filter( $schema['properties'], array( $this, 'filter_writable_props' ) ) );
345
346
		// Handle all writable props
347
		foreach ( $data_keys as $key ) {
348
			$value = $request[ $key ];
349
350
			if ( ! is_null( $value ) ) {
351
				switch ( $key ) {
352
					case 'billing' :
353
					case 'shipping' :
354
						$this->update_address( $order, $value, $key );
355
						break;
356
					case 'line_items' :
357
					case 'shipping_lines' :
358
					case 'fee_lines' :
359
					case 'coupon_lines' :
360
						if ( is_array( $value ) ) {
361
							foreach ( $value as $item ) {
362
								if ( $this->item_is_null( $item ) || ( isset( $item['quantity'] ) && 0 === $item['quantity'] ) ) {
363
									$order->remove_item( $item['id'] );
364
								} else {
365
									$this->set_item( $order, $key, $item );
366
								}
367
							}
368
						}
369
						break;
370
					case 'meta_data' :
371
						if ( is_array( $value ) ) {
372
							foreach ( $value as $meta ) {
373
								$order->update_meta_data( $meta['key'], $meta['value'], $meta['meta_id'] );
374
							}
375
						}
376
						break;
377
					default :
378
						if ( is_callable( array( $order, "set_{$key}" ) ) ) {
379
							$order->{"set_{$key}"}( $value );
380
						}
381
						break;
382
				}
383
			}
384
		}
385
386
		/**
387
		 * Filter the data for the insert.
388
		 *
389
		 * The dynamic portion of the hook name, $this->post_type, refers to post_type of the post being
390
		 * prepared for the response.
391
		 *
392
		 * @param WC_Order           $order      The prder object.
393
		 * @param WP_REST_Request    $request    Request object.
394
		 */
395
		return apply_filters( "woocommerce_rest_pre_insert_{$this->post_type}", $order, $request );
396
	}
397
398
	/**
399
	 * Create base WC Order object.
400
	 * @deprecated 2.7.0
401
	 * @param array $data
402
	 * @return WC_Order
403
	 */
404
	protected function create_base_order( $data ) {
405
		return wc_create_order( $data );
406
	}
407
408
	/**
409
	 * Only reutrn writeable props from schema.
410
	 * @param  array $schema
411
	 * @return bool
412
	 */
413
	protected function filter_writable_props( $schema ) {
414
		return empty( $schema['readonly'] );
415
	}
416
417
	/**
418
	 * Create order.
419
	 *
420
	 * @param WP_REST_Request $request Full details about the request.
421
	 * @return int|WP_Error
422
	 */
423
	protected function create_order( $request ) {
424
		try {
425
			// Make sure customer exists.
426
			if ( ! is_null( $request['customer_id'] ) && 0 !== $request['customer_id'] && false === get_user_by( 'id', $request['customer_id'] ) ) {
427
				throw new WC_REST_Exception( 'woocommerce_rest_invalid_customer_id',__( 'Customer ID is invalid.', 'woocommerce' ), 400 );
428
			}
429
430
			$order = $this->prepare_item_for_database( $request );
431
			$order->set_created_via( 'rest-api' );
432
			$order->set_prices_include_tax( 'yes' === get_option( 'woocommerce_prices_include_tax' ) );
433
			$order->calculate_totals();
434
			$order->save();
435
436
			// Handle set paid
437
			if ( true === $request['set_paid'] ) {
438
				$order->payment_complete( $request['transaction_id'] );
439
			}
440
441
			return $order->get_id();
442
		} catch ( Exception $e ) {
443
			return new WP_Error( $e->getErrorCode(), $e->getMessage(), array( 'status' => $e->getCode() ) );
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Exception as the method getErrorCode() does only exist in the following sub-classes of Exception: WC_CLI_Exception, WC_Data_Exception, WC_REST_Exception. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
444
		}
445
	}
446
447
	/**
448
	 * Update order.
449
	 *
450
	 * @param WP_REST_Request $request Full details about the request.
451
	 * @return int|WP_Error
452
	 */
453
	protected function update_order( $request ) {
454
		try {
455
			$order = $this->prepare_item_for_database( $request );
456
			$order->save();
457
458
			// Handle set paid
459
			if ( $order->needs_payment() && true === $request['set_paid'] ) {
460
				$order->payment_complete( $request['transaction_id'] );
461
			}
462
463
			// If items have changed, recalculate order totals.
464
			if ( isset( $request[ 'billing' ], $request[ 'shipping' ], $request[ 'line_items' ], $request[ 'shipping' ], $request[ 'fee' ], $request[ 'coupon' ] ) ) {
465
				$order->calculate_totals();
466
			}
467
468
			return $order->get_id();
469
		} catch ( WC_REST_Exception $e ) {
470
			return new WP_Error( $e->getErrorCode(), $e->getMessage(), array( 'status' => $e->getCode() ) );
471
		}
472
	}
473
474
	/**
475
	 * Update address.
476
	 *
477
	 * @param WC_Order $order
478
	 * @param array $posted
479
	 * @param string $type
480
	 */
481
	protected function update_address( $order, $posted, $type = 'billing' ) {
482
		foreach ( $posted as $key => $value ) {
483
			if ( is_callable( array( $order, "set_{$type}_{$key}" ) ) ) {
484
				$order->{"set_{$type}_{$key}"}( $value );
485
			}
486
		}
487
	}
488
489
	/**
490
	 * Create or update a line item.
491
	 *
492
	 * @param array $posted Line item data.
493
	 * @param string $action 'create' to add line item or 'update' to update it.
494
	 * @throws WC_REST_Exception Invalid data, server error.
495
	 */
496
	protected function prepare_line_items( $posted, $action = 'create' ) {
497
		// Product is always required.
498
		if ( empty( $posted['product_id'] ) && empty( $posted['sku'] ) && empty( $posted['variation_id'] ) ) {
499
			throw new WC_REST_Exception( 'woocommerce_rest_required_product_reference', __( 'Product ID or SKU is required.', 'woocommerce' ), 400 );
500
		}
501
502
		// Get product from ID or sku
503
		if ( ! empty( $posted['sku'] ) ) {
504
			$product_id = (int) wc_get_product_id_by_sku( $posted['sku'] );
505
		} elseif ( ! empty( $posted['product_id'] ) && empty( $posted['variation_id'] ) ) {
506
			$product_id = (int) $posted['product_id'];
507
		} elseif ( ! empty( $posted['variation_id'] ) ) {
508
			$product_id = (int) $posted['variation_id'];
509
		}
510
511
		$item    = new WC_Order_Item_Product( ! empty( $posted['id'] ) ? $posted['id'] : '' );
512
		$product = wc_get_product( $product_id );
513
514
		if ( $product !== $item->get_product() ) {
515
			$item->set_product( $product );
516
517
			if ( 'create' === $action ) {
518
				$qty = isset( $posted['quantity'] ) ? $posted['quantity'] : 1;
519
				$item->set_total( $product->get_price() * $qty );
520
				$item->set_subtotal( $product->get_price() * $qty );
521
			}
522
		}
523
524
		if ( isset( $posted['name'] ) ) {
525
			$item->set_name( $posted['name'] );
526
		}
527
528
		if ( isset( $posted['quantity'] ) ) {
529
			$item->set_quantity( $posted['quantity'] );
530
		}
531
532
		if ( isset( $posted['total'] ) ) {
533
			$item->set_total( floatval( $posted['total'] ) );
534
		}
535
536
		if ( isset( $posted['subtotal'] ) ) {
537
			$item->set_subtotal( floatval( $posted['subtotal'] ) );
538
		}
539
540
		if ( isset( $posted['tax_class'] ) ) {
541
			$item->set_tax_class( $posted['tax_class'] );
542
		}
543
544
		return $item;
545
	}
546
547
	/**
548
	 * Create or update an order shipping method.
549
	 *
550
	 * @param $posted $shipping Item data.
0 ignored issues
show
Documentation introduced by
The doc-type $posted could not be parsed: Unknown type name "$posted" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
Bug introduced by
There is no parameter named $shipping. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
551
	 * @param string $action 'create' to add shipping or 'update' to update it.
552
	 * @throws WC_REST_Exception Invalid data, server error.
553
	 */
554
	protected function prepare_shipping_lines( $posted, $action ) {
555
		$item = new WC_Order_Item_Shipping( ! empty( $posted['id'] ) ? $posted['id'] : '' );
556
557
		if ( 'create' === $action ) {
558
			if ( empty( $posted['method_id'] ) ) {
559
				throw new WC_REST_Exception( 'woocommerce_rest_invalid_shipping_item', __( 'Shipping method ID is required.', 'woocommerce' ), 400 );
560
			}
561
		}
562
563
		if ( isset( $posted['method_id'] ) ) {
564
			$item->set_method_id( $posted['method_id'] );
565
		}
566
567
		if ( isset( $posted['method_title'] ) ) {
568
			$item->set_method_title( $posted['method_title'] );
569
		}
570
571
		if ( isset( $posted['total'] ) ) {
572
			$item->set_total( floatval( $posted['total'] ) );
573
		}
574
575
		return $item;
576
	}
577
578
	/**
579
	 * Create or update an order fee.
580
	 *
581
	 * @param array $posted Item data.
582
	 * @param string $action 'create' to add fee or 'update' to update it.
583
	 * @throws WC_REST_Exception Invalid data, server error.
584
	 */
585
	protected function prepare_fee_lines( $posted, $action ) {
586
		$item = new WC_Order_Item_Fee( ! empty( $posted['id'] ) ? $posted['id'] : '' );
587
588 View Code Duplication
		if ( 'create' === $action ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
589
			if ( empty( $posted['name'] ) ) {
590
				throw new WC_REST_Exception( 'woocommerce_rest_invalid_fee_item', __( 'Fee name is required.', 'woocommerce' ), 400 );
591
			}
592
		}
593
594
		if ( isset( $posted['name'] ) ) {
595
			$item->set_name( $posted['name'] );
596
		}
597
598
		if ( isset( $posted['tax_class'] ) ) {
599
			$item->set_tax_class( $posted['tax_class'] );
600
		}
601
602
		if ( isset( $posted['tax_status'] ) ) {
603
			$item->set_tax_status( $posted['tax_status'] );
604
		}
605
606
		if ( isset( $posted['total'] ) ) {
607
			$item->set_total( floatval( $posted['total'] ) );
608
		}
609
610
		return $item;
611
	}
612
613
	/**
614
	 * Create or update an order coupon.
615
	 *
616
	 * @param array $posted Item data.
617
	 * @param string $action 'create' to add coupon or 'update' to update it.
618
	 * @throws WC_REST_Exception Invalid data, server error.
619
	 */
620
	protected function prepare_coupon_lines( $posted, $action ) {
621
		$item = new WC_Order_Item_Coupon( ! empty( $posted['id'] ) ? $posted['id'] : '' );
622
623 View Code Duplication
		if ( 'create' === $action ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
624
			if ( empty( $posted['code'] ) ) {
625
				throw new WC_REST_Exception( 'woocommerce_rest_invalid_coupon_coupon', __( 'Coupon code is required.', 'woocommerce' ), 400 );
626
			}
627
		}
628
629
		if ( isset( $posted['code'] ) ) {
630
			$item->set_code( $posted['code'] );
631
		}
632
633
		if ( isset( $posted['discount'] ) ) {
634
			$item->set_discount( floatval( $posted['discount'] ) );
635
		}
636
637
		return $item;
638
	}
639
640
	/**
641
	 * Wrapper method to create/update order items.
642
	 * When updating, the item ID provided is checked to ensure it is associated
643
	 * with the order.
644
	 *
645
	 * @param WC_Order $order order
646
	 * @param string $item_type
647
	 * @param array $posted item provided in the request body
648
	 * @throws WC_REST_Exception If item ID is not associated with order
649
	 */
650
	protected function set_item( $order, $item_type, $posted ) {
651
		global $wpdb;
652
653
		if ( ! empty( $posted['id'] ) ) {
654
			$action = 'update';
655
		} else {
656
			$action = 'create';
657
		}
658
659
		$method = 'prepare_' . $item_type;
660
661
		// Verify provided line item ID is associated with order.
662
		if ( 'update' === $action ) {
663
			$result = $wpdb->get_row(
664
				$wpdb->prepare( "SELECT * FROM {$wpdb->prefix}woocommerce_order_items WHERE order_item_id = %d AND order_id = %d",
665
				absint( $posted['id'] ),
666
				absint( $order->get_id() )
667
			) );
668
			if ( is_null( $result ) ) {
669
				throw new WC_REST_Exception( 'woocommerce_rest_invalid_item_id', __( 'Order item ID provided is not associated with order.', 'woocommerce' ), 400 );
670
			}
671
		}
672
673
		// Prepare item data
674
		$item = $this->$method( $posted, $action );
675
676
		// Save or add to order
677
		if ( 'create' === $action ) {
678
			$order->add_item( $item );
679
		} else {
680
			$item->save();
681
		}
682
	}
683
684
	/**
685
	 * Helper method to check if the resource ID associated with the provided item is null.
686
	 * Items can be deleted by setting the resource ID to null.
687
	 *
688
	 * @param array $item Item provided in the request body.
689
	 * @return bool True if the item resource ID is null, false otherwise.
690
	 */
691
	protected function item_is_null( $item ) {
692
		$keys = array( 'product_id', 'method_id', 'title', 'code' );
693
694
		foreach ( $keys as $key ) {
695
			if ( array_key_exists( $key, $item ) && is_null( $item[ $key ] ) ) {
696
				return true;
697
			}
698
		}
699
700
		return false;
701
	}
702
703
	/**
704
	 * Create a single item.
705
	 *
706
	 * @param WP_REST_Request $request Full details about the request.
707
	 * @return WP_Error|WP_REST_Response
708
	 */
709
	public function create_item( $request ) {
710
		if ( ! empty( $request['id'] ) ) {
711
			return new WP_Error( "woocommerce_rest_{$this->post_type}_exists", sprintf( __( 'Cannot create existing %s.', 'woocommerce' ), $this->post_type ), array( 'status' => 400 ) );
712
		}
713
714
		$order_id = $this->create_order( $request );
715
		if ( is_wp_error( $order_id ) ) {
716
			return $order_id;
717
		}
718
719
		$post = get_post( $order_id );
720
		$this->update_additional_fields_for_object( $post, $request );
721
722
		/**
723
		 * Fires after a single item is created or updated via the REST API.
724
		 *
725
		 * @param object          $post      Inserted object (not a WP_Post object).
726
		 * @param WP_REST_Request $request   Request object.
727
		 * @param boolean         $creating  True when creating item, false when updating.
728
		 */
729
		do_action( "woocommerce_rest_insert_{$this->post_type}", $post, $request, true );
730
		$request->set_param( 'context', 'edit' );
731
		$response = $this->prepare_item_for_response( $post, $request );
732
		$response = rest_ensure_response( $response );
733
		$response->set_status( 201 );
734
		$response->header( 'Location', rest_url( sprintf( '/%s/%s/%d', $this->namespace, $this->rest_base, $post->ID ) ) );
735
736
		return $response;
737
	}
738
739
	/**
740
	 * Update a single order.
741
	 *
742
	 * @param WP_REST_Request $request Full details about the request.
743
	 * @return WP_Error|WP_REST_Response
744
	 */
745
	public function update_item( $request ) {
746
		try {
747
			$post_id = (int) $request['id'];
748
749 View Code Duplication
			if ( empty( $post_id ) || $this->post_type !== get_post_type( $post_id ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
750
				return new WP_Error( "woocommerce_rest_{$this->post_type}_invalid_id", __( 'ID is invalid.', 'woocommerce' ), array( 'status' => 400 ) );
751
			}
752
753
			$order_id = $this->update_order( $request );
754
			if ( is_wp_error( $order_id ) ) {
755
				return $order_id;
756
			}
757
758
			$post = get_post( $order_id );
759
			$this->update_additional_fields_for_object( $post, $request );
760
761
			/**
762
			 * Fires after a single item is created or updated via the REST API.
763
			 *
764
			 * @param object          $post      Inserted object (not a WP_Post object).
765
			 * @param WP_REST_Request $request   Request object.
766
			 * @param boolean         $creating  True when creating item, false when updating.
767
			 */
768
			do_action( "woocommerce_rest_insert_{$this->post_type}", $post, $request, false );
769
			$request->set_param( 'context', 'edit' );
770
			$response = $this->prepare_item_for_response( $post, $request );
771
			return rest_ensure_response( $response );
772
773
		} catch ( Exception $e ) {
774
			return new WP_Error( $e->getErrorCode(), $e->getMessage(), array( 'status' => $e->getCode() ) );
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Exception as the method getErrorCode() does only exist in the following sub-classes of Exception: WC_CLI_Exception, WC_Data_Exception, WC_REST_Exception. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
775
		}
776
	}
777
778
	/**
779
	 * Get order statuses without prefixes.
780
	 * @return array
781
	 */
782
	protected function get_order_statuses() {
783
		$order_statuses = array();
784
785
		foreach ( array_keys( wc_get_order_statuses() ) as $status ) {
786
			$order_statuses[] = str_replace( 'wc-', '', $status );
787
		}
788
789
		return $order_statuses;
790
	}
791
792
	/**
793
	 * Get the Order's schema, conforming to JSON Schema.
794
	 *
795
	 * @return array
796
	 */
797
	public function get_item_schema() {
798
		$schema = array(
799
			'$schema'    => 'http://json-schema.org/draft-04/schema#',
800
			'title'      => $this->post_type,
801
			'type'       => 'object',
802
			'properties' => array(
803
				'id' => array(
804
					'description' => __( 'Unique identifier for the resource.', 'woocommerce' ),
805
					'type'        => 'integer',
806
					'context'     => array( 'view', 'edit' ),
807
					'readonly'    => true,
808
				),
809
				'parent_id' => array(
810
					'description' => __( 'Parent order ID.', 'woocommerce' ),
811
					'type'        => 'integer',
812
					'context'     => array( 'view', 'edit' ),
813
				),
814
				'status' => array(
815
					'description' => __( 'Order status.', 'woocommerce' ),
816
					'type'        => 'string',
817
					'default'     => 'pending',
818
					'enum'        => $this->get_order_statuses(),
819
					'context'     => array( 'view', 'edit' ),
820
				),
821
				'order_key' => array(
822
					'description' => __( 'Order key.', 'woocommerce' ),
823
					'type'        => 'string',
824
					'context'     => array( 'view', 'edit' ),
825
					'readonly'    => true,
826
				),
827
				'currency' => array(
828
					'description' => __( 'Currency the order was created with, in ISO format.', 'woocommerce' ),
829
					'type'        => 'string',
830
					'default'     => get_woocommerce_currency(),
831
					'enum'        => array_keys( get_woocommerce_currencies() ),
832
					'context'     => array( 'view', 'edit' ),
833
				),
834
				'version' => array(
835
					'description' => __( 'Version of WooCommerce when the order was made.', 'woocommerce' ),
836
					'type'        => 'integer',
837
					'context'     => array( 'view', 'edit' ),
838
					'readonly'    => true,
839
				),
840
				'prices_include_tax' => array(
841
					'description' => __( 'True the prices included tax during checkout.', 'woocommerce' ),
842
					'type'        => 'boolean',
843
					'context'     => array( 'view', 'edit' ),
844
					'readonly'    => true,
845
				),
846
				'date_created' => array(
847
					'description' => __( "The date the order was created, in the site's timezone.", 'woocommerce' ),
848
					'type'        => 'date-time',
849
					'context'     => array( 'view', 'edit' ),
850
				),
851
				'date_modified' => array(
852
					'description' => __( "The date the order was last modified, in the site's timezone.", 'woocommerce' ),
853
					'type'        => 'date-time',
854
					'context'     => array( 'view', 'edit' ),
855
					'readonly'    => true,
856
				),
857
				'customer_id' => array(
858
					'description' => __( 'User ID who owns the order. 0 for guests.', 'woocommerce' ),
859
					'type'        => 'integer',
860
					'default'     => 0,
861
					'context'     => array( 'view', 'edit' ),
862
				),
863
				'discount_total' => array(
864
					'description' => __( 'Total discount amount for the order.', 'woocommerce' ),
865
					'type'        => 'string',
866
					'context'     => array( 'view', 'edit' ),
867
					'readonly'    => true,
868
				),
869
				'discount_tax' => array(
870
					'description' => __( 'Total discount tax amount for the order.', 'woocommerce' ),
871
					'type'        => 'string',
872
					'context'     => array( 'view', 'edit' ),
873
					'readonly'    => true,
874
				),
875
				'shipping_total' => array(
876
					'description' => __( 'Total shipping amount for the order.', 'woocommerce' ),
877
					'type'        => 'string',
878
					'context'     => array( 'view', 'edit' ),
879
					'readonly'    => true,
880
				),
881
				'shipping_tax' => array(
882
					'description' => __( 'Total shipping tax amount for the order.', 'woocommerce' ),
883
					'type'        => 'string',
884
					'context'     => array( 'view', 'edit' ),
885
					'readonly'    => true,
886
				),
887
				'cart_tax' => array(
888
					'description' => __( 'Sum of line item taxes only.', 'woocommerce' ),
889
					'type'        => 'string',
890
					'context'     => array( 'view', 'edit' ),
891
					'readonly'    => true,
892
				),
893
				'total' => array(
894
					'description' => __( 'Grand total.', 'woocommerce' ),
895
					'type'        => 'string',
896
					'context'     => array( 'view', 'edit' ),
897
					'readonly'    => true,
898
				),
899
				'total_tax' => array(
900
					'description' => __( 'Sum of all taxes.', 'woocommerce' ),
901
					'type'        => 'string',
902
					'context'     => array( 'view', 'edit' ),
903
					'readonly'    => true,
904
				),
905
				'billing' => array(
906
					'description' => __( 'Billing address.', 'woocommerce' ),
907
					'type'        => 'array',
908
					'context'     => array( 'view', 'edit' ),
909
					'properties'  => array(
910
						'first_name' => array(
911
							'description' => __( 'First name.', 'woocommerce' ),
912
							'type'        => 'string',
913
							'context'     => array( 'view', 'edit' ),
914
						),
915
						'last_name' => array(
916
							'description' => __( 'Last name.', 'woocommerce' ),
917
							'type'        => 'string',
918
							'context'     => array( 'view', 'edit' ),
919
						),
920
						'company' => array(
921
							'description' => __( 'Company name.', 'woocommerce' ),
922
							'type'        => 'string',
923
							'context'     => array( 'view', 'edit' ),
924
						),
925
						'address_1' => array(
926
							'description' => __( 'Address line 1.', 'woocommerce' ),
927
							'type'        => 'string',
928
							'context'     => array( 'view', 'edit' ),
929
						),
930
						'address_2' => array(
931
							'description' => __( 'Address line 2.', 'woocommerce' ),
932
							'type'        => 'string',
933
							'context'     => array( 'view', 'edit' ),
934
						),
935
						'city' => array(
936
							'description' => __( 'City name.', 'woocommerce' ),
937
							'type'        => 'string',
938
							'context'     => array( 'view', 'edit' ),
939
						),
940
						'state' => array(
941
							'description' => __( 'ISO code or name of the state, province or district.', 'woocommerce' ),
942
							'type'        => 'string',
943
							'context'     => array( 'view', 'edit' ),
944
						),
945
						'postcode' => array(
946
							'description' => __( 'Postal code.', 'woocommerce' ),
947
							'type'        => 'string',
948
							'context'     => array( 'view', 'edit' ),
949
						),
950
						'country' => array(
951
							'description' => __( 'Country code in ISO 3166-1 alpha-2 format.', 'woocommerce' ),
952
							'type'        => 'string',
953
							'context'     => array( 'view', 'edit' ),
954
						),
955
						'email' => array(
956
							'description' => __( 'Email address.', 'woocommerce' ),
957
							'type'        => 'string',
958
							'format'      => 'email',
959
							'context'     => array( 'view', 'edit' ),
960
						),
961
						'phone' => array(
962
							'description' => __( 'Phone number.', 'woocommerce' ),
963
							'type'        => 'string',
964
							'context'     => array( 'view', 'edit' ),
965
						),
966
					),
967
				),
968
				'shipping' => array(
969
					'description' => __( 'Shipping address.', 'woocommerce' ),
970
					'type'        => 'array',
971
					'context'     => array( 'view', 'edit' ),
972
					'properties'  => array(
973
						'first_name' => array(
974
							'description' => __( 'First name.', 'woocommerce' ),
975
							'type'        => 'string',
976
							'context'     => array( 'view', 'edit' ),
977
						),
978
						'last_name' => array(
979
							'description' => __( 'Last name.', 'woocommerce' ),
980
							'type'        => 'string',
981
							'context'     => array( 'view', 'edit' ),
982
						),
983
						'company' => array(
984
							'description' => __( 'Company name.', 'woocommerce' ),
985
							'type'        => 'string',
986
							'context'     => array( 'view', 'edit' ),
987
						),
988
						'address_1' => array(
989
							'description' => __( 'Address line 1.', 'woocommerce' ),
990
							'type'        => 'string',
991
							'context'     => array( 'view', 'edit' ),
992
						),
993
						'address_2' => array(
994
							'description' => __( 'Address line 2.', 'woocommerce' ),
995
							'type'        => 'string',
996
							'context'     => array( 'view', 'edit' ),
997
						),
998
						'city' => array(
999
							'description' => __( 'City name.', 'woocommerce' ),
1000
							'type'        => 'string',
1001
							'context'     => array( 'view', 'edit' ),
1002
						),
1003
						'state' => array(
1004
							'description' => __( 'ISO code or name of the state, province or district.', 'woocommerce' ),
1005
							'type'        => 'string',
1006
							'context'     => array( 'view', 'edit' ),
1007
						),
1008
						'postcode' => array(
1009
							'description' => __( 'Postal code.', 'woocommerce' ),
1010
							'type'        => 'string',
1011
							'context'     => array( 'view', 'edit' ),
1012
						),
1013
						'country' => array(
1014
							'description' => __( 'Country code in ISO 3166-1 alpha-2 format.', 'woocommerce' ),
1015
							'type'        => 'string',
1016
							'context'     => array( 'view', 'edit' ),
1017
						),
1018
					),
1019
				),
1020
				'payment_method' => array(
1021
					'description' => __( 'Payment method ID.', 'woocommerce' ),
1022
					'type'        => 'string',
1023
					'context'     => array( 'view', 'edit' ),
1024
				),
1025
				'payment_method_title' => array(
1026
					'description' => __( 'Payment method title.', 'woocommerce' ),
1027
					'type'        => 'string',
1028
					'context'     => array( 'view', 'edit' ),
1029
				),
1030
				'transaction_id' => array(
1031
					'description' => __( 'Unique transaction ID.', 'woocommerce' ),
1032
					'type'        => 'string',
1033
					'context'     => array( 'view', 'edit' ),
1034
				),
1035
				'customer_ip_address' => array(
1036
					'description' => __( "Customer's IP address.", 'woocommerce' ),
1037
					'type'        => 'string',
1038
					'context'     => array( 'view', 'edit' ),
1039
					'readonly'    => true,
1040
				),
1041
				'customer_user_agent' => array(
1042
					'description' => __( 'User agent of the customer.', 'woocommerce' ),
1043
					'type'        => 'string',
1044
					'context'     => array( 'view', 'edit' ),
1045
					'readonly'    => true,
1046
				),
1047
				'created_via' => array(
1048
					'description' => __( 'Shows where the order was created.', 'woocommerce' ),
1049
					'type'        => 'string',
1050
					'context'     => array( 'view', 'edit' ),
1051
					'readonly'    => true,
1052
				),
1053
				'customer_note' => array(
1054
					'description' => __( 'Note left by customer during checkout.', 'woocommerce' ),
1055
					'type'        => 'string',
1056
					'context'     => array( 'view', 'edit' ),
1057
				),
1058
				'date_completed' => array(
1059
					'description' => __( "The date the order was completed, in the site's timezone.", 'woocommerce' ),
1060
					'type'        => 'date-time',
1061
					'context'     => array( 'view', 'edit' ),
1062
				),
1063
				'date_paid' => array(
1064
					'description' => __( "The date the order has been paid, in the site's timezone.", 'woocommerce' ),
1065
					'type'        => 'date-time',
1066
					'context'     => array( 'view', 'edit' ),
1067
				),
1068
				'cart_hash' => array(
1069
					'description' => __( 'MD5 hash of cart items to ensure orders are not modified.', 'woocommerce' ),
1070
					'type'        => 'string',
1071
					'context'     => array( 'view', 'edit' ),
1072
					'readonly'    => true,
1073
				),
1074
				'number' => array(
1075
					'description' => __( 'Order number.', 'woocommerce' ),
1076
					'type'        => 'string',
1077
					'context'     => array( 'view', 'edit' ),
1078
					'readonly'    => true,
1079
				),
1080
				'meta_data' => array(
1081
					'description' => __( 'Order meta data.', 'woocommerce' ),
1082
					'type'        => 'array',
1083
					'context'     => array( 'view', 'edit' ),
1084
					'properties'  => array(
1085
						'key' => array(
1086
							'description' => __( 'Meta key.', 'woocommerce' ),
1087
							'type'        => 'string',
1088
							'context'     => array( 'view', 'edit' ),
1089
						),
1090
						'value' => array(
1091
							'description' => __( 'Meta value.', 'woocommerce' ),
1092
							'type'        => 'string',
1093
							'context'     => array( 'view', 'edit' ),
1094
						),
1095
						'meta_id' => array(
1096
							'description' => __( 'Meta ID.', 'woocommerce' ),
1097
							'type'        => 'int',
1098
							'context'     => array( 'view', 'edit' ),
1099
							'readonly'    => true,
1100
						),
1101
					),
1102
				),
1103
				'line_items' => array(
1104
					'description' => __( 'Line items data.', 'woocommerce' ),
1105
					'type'        => 'array',
1106
					'context'     => array( 'view', 'edit' ),
1107
					'properties'  => array(
1108
						'id' => array(
1109
							'description' => __( 'Item ID.', 'woocommerce' ),
1110
							'type'        => 'integer',
1111
							'context'     => array( 'view', 'edit' ),
1112
							'readonly'    => true,
1113
						),
1114
						'name' => array(
1115
							'description' => __( 'Product name.', 'woocommerce' ),
1116
							'type'        => 'string',
1117
							'context'     => array( 'view', 'edit' ),
1118
						),
1119
						'product_id' => array(
1120
							'description' => __( 'Product ID.', 'woocommerce' ),
1121
							'type'        => 'integer',
1122
							'context'     => array( 'view', 'edit' ),
1123
						),
1124
						'variation_id' => array(
1125
							'description' => __( 'Variation ID, if applicable.', 'woocommerce' ),
1126
							'type'        => 'integer',
1127
							'context'     => array( 'view', 'edit' ),
1128
						),
1129
						'quantity' => array(
1130
							'description' => __( 'Quantity ordered.', 'woocommerce' ),
1131
							'type'        => 'integer',
1132
							'context'     => array( 'view', 'edit' ),
1133
						),
1134
						'tax_class' => array(
1135
							'description' => __( 'Tax class of product.', 'woocommerce' ),
1136
							'type'        => 'integer',
1137
							'context'     => array( 'view', 'edit' ),
1138
						),
1139
						'subtotal' => array(
1140
							'description' => __( 'Line subtotal (before discounts).', 'woocommerce' ),
1141
							'type'        => 'string',
1142
							'context'     => array( 'view', 'edit' ),
1143
						),
1144
						'subtotal_tax' => array(
1145
							'description' => __( 'Line subtotal tax (before discounts).', 'woocommerce' ),
1146
							'type'        => 'string',
1147
							'context'     => array( 'view', 'edit' ),
1148
							'readonly'    => true,
1149
						),
1150
						'total' => array(
1151
							'description' => __( 'Line total (after discounts).', 'woocommerce' ),
1152
							'type'        => 'string',
1153
							'context'     => array( 'view', 'edit' ),
1154
						),
1155
						'total_tax' => array(
1156
							'description' => __( 'Line total tax (after discounts).', 'woocommerce' ),
1157
							'type'        => 'string',
1158
							'context'     => array( 'view', 'edit' ),
1159
							'readonly'    => true,
1160
						),
1161
						'taxes' => array(
1162
							'description' => __( 'Line taxes.', 'woocommerce' ),
1163
							'type'        => 'array',
1164
							'context'     => array( 'view', 'edit' ),
1165
							'readonly'    => true,
1166
							'properties'  => array(
1167
								'id' => array(
1168
									'description' => __( 'Tax rate ID.', 'woocommerce' ),
1169
									'type'        => 'integer',
1170
									'context'     => array( 'view', 'edit' ),
1171
								),
1172
								'total' => array(
1173
									'description' => __( 'Tax total.', 'woocommerce' ),
1174
									'type'        => 'string',
1175
									'context'     => array( 'view', 'edit' ),
1176
								),
1177
								'subtotal' => array(
1178
									'description' => __( 'Tax subtotal.', 'woocommerce' ),
1179
									'type'        => 'string',
1180
									'context'     => array( 'view', 'edit' ),
1181
								),
1182
							),
1183
						),
1184
						'meta_data' => array(
1185
							'description' => __( 'Order item meta data.', 'woocommerce' ),
1186
							'type'        => 'array',
1187
							'context'     => array( 'view', 'edit' ),
1188
							'properties'  => array(
1189
								'key' => array(
1190
									'description' => __( 'Meta key.', 'woocommerce' ),
1191
									'type'        => 'string',
1192
									'context'     => array( 'view', 'edit' ),
1193
								),
1194
								'value' => array(
1195
									'description' => __( 'Meta value.', 'woocommerce' ),
1196
									'type'        => 'string',
1197
									'context'     => array( 'view', 'edit' ),
1198
								),
1199
								'meta_id' => array(
1200
									'description' => __( 'Meta ID.', 'woocommerce' ),
1201
									'type'        => 'int',
1202
									'context'     => array( 'view', 'edit' ),
1203
									'readonly'    => true,
1204
								),
1205
							),
1206
						),
1207
						'sku' => array(
1208
							'description' => __( 'Product SKU.', 'woocommerce' ),
1209
							'type'        => 'string',
1210
							'context'     => array( 'view', 'edit' ),
1211
							'readonly'    => true,
1212
						),
1213
						'price' => array(
1214
							'description' => __( 'Product price.', 'woocommerce' ),
1215
							'type'        => 'string',
1216
							'context'     => array( 'view', 'edit' ),
1217
							'readonly'    => true,
1218
						),
1219
						'meta' => array(
1220
							'description' => __( 'Order item meta data (formatted).', 'woocommerce' ),
1221
							'type'        => 'array',
1222
							'context'     => array( 'view', 'edit' ),
1223
							'readonly'    => true,
1224
							'properties'  => array(
1225
								'key' => array(
1226
									'description' => __( 'Meta key.', 'woocommerce' ),
1227
									'type'        => 'string',
1228
									'context'     => array( 'view', 'edit' ),
1229
									'readonly'    => true,
1230
								),
1231
								'label' => array(
1232
									'description' => __( 'Meta label.', 'woocommerce' ),
1233
									'type'        => 'string',
1234
									'context'     => array( 'view', 'edit' ),
1235
									'readonly'    => true,
1236
								),
1237
								'value' => array(
1238
									'description' => __( 'Meta value.', 'woocommerce' ),
1239
									'type'        => 'string',
1240
									'context'     => array( 'view', 'edit' ),
1241
									'readonly'    => true,
1242
								),
1243
							),
1244
						),
1245
					),
1246
				),
1247
				'tax_lines' => array(
1248
					'description' => __( 'Tax lines data.', 'woocommerce' ),
1249
					'type'        => 'array',
1250
					'context'     => array( 'view', 'edit' ),
1251
					'readonly'    => true,
1252
					'properties'  => array(
1253
						'id' => array(
1254
							'description' => __( 'Item ID.', 'woocommerce' ),
1255
							'type'        => 'integer',
1256
							'context'     => array( 'view', 'edit' ),
1257
							'readonly'    => true,
1258
						),
1259
						'rate_code' => array(
1260
							'description' => __( 'Tax rate code.', 'woocommerce' ),
1261
							'type'        => 'string',
1262
							'context'     => array( 'view', 'edit' ),
1263
							'readonly'    => true,
1264
						),
1265
						'rate_id' => array(
1266
							'description' => __( 'Tax rate ID.', 'woocommerce' ),
1267
							'type'        => 'string',
1268
							'context'     => array( 'view', 'edit' ),
1269
							'readonly'    => true,
1270
						),
1271
						'label' => array(
1272
							'description' => __( 'Tax rate label.', 'woocommerce' ),
1273
							'type'        => 'string',
1274
							'context'     => array( 'view', 'edit' ),
1275
							'readonly'    => true,
1276
						),
1277
						'compound' => array(
1278
							'description' => __( 'Show if is a compound tax rate.', 'woocommerce' ),
1279
							'type'        => 'boolean',
1280
							'context'     => array( 'view', 'edit' ),
1281
							'readonly'    => true,
1282
						),
1283
						'tax_total' => array(
1284
							'description' => __( 'Tax total (not including shipping taxes).', 'woocommerce' ),
1285
							'type'        => 'string',
1286
							'context'     => array( 'view', 'edit' ),
1287
							'readonly'    => true,
1288
						),
1289
						'shipping_tax_total' => array(
1290
							'description' => __( 'Shipping tax total.', 'woocommerce' ),
1291
							'type'        => 'string',
1292
							'context'     => array( 'view', 'edit' ),
1293
							'readonly'    => true,
1294
						),
1295
						'meta_data' => array(
1296
							'description' => __( 'Order item meta data.', 'woocommerce' ),
1297
							'type'        => 'array',
1298
							'context'     => array( 'view', 'edit' ),
1299
							'properties'  => array(
1300
								'key' => array(
1301
									'description' => __( 'Meta key.', 'woocommerce' ),
1302
									'type'        => 'string',
1303
									'context'     => array( 'view', 'edit' ),
1304
								),
1305
								'value' => array(
1306
									'description' => __( 'Meta value.', 'woocommerce' ),
1307
									'type'        => 'string',
1308
									'context'     => array( 'view', 'edit' ),
1309
								),
1310
								'meta_id' => array(
1311
									'description' => __( 'Meta ID.', 'woocommerce' ),
1312
									'type'        => 'int',
1313
									'context'     => array( 'view', 'edit' ),
1314
									'readonly'    => true,
1315
								),
1316
							),
1317
						),
1318
					),
1319
				),
1320
				'shipping_lines' => array(
1321
					'description' => __( 'Shipping lines data.', 'woocommerce' ),
1322
					'type'        => 'array',
1323
					'context'     => array( 'view', 'edit' ),
1324
					'properties'  => array(
1325
						'id' => array(
1326
							'description' => __( 'Item ID.', 'woocommerce' ),
1327
							'type'        => 'integer',
1328
							'context'     => array( 'view', 'edit' ),
1329
							'readonly'    => true,
1330
						),
1331
						'method_title' => array(
1332
							'description' => __( 'Shipping method name.', 'woocommerce' ),
1333
							'type'        => 'string',
1334
							'context'     => array( 'view', 'edit' ),
1335
						),
1336
						'method_id' => array(
1337
							'description' => __( 'Shipping method ID.', 'woocommerce' ),
1338
							'type'        => 'string',
1339
							'context'     => array( 'view', 'edit' ),
1340
						),
1341
						'total' => array(
1342
							'description' => __( 'Line total (after discounts).', 'woocommerce' ),
1343
							'type'        => 'string',
1344
							'context'     => array( 'view', 'edit' ),
1345
						),
1346
						'total_tax' => array(
1347
							'description' => __( 'Line total tax (after discounts).', 'woocommerce' ),
1348
							'type'        => 'string',
1349
							'context'     => array( 'view', 'edit' ),
1350
							'readonly'    => true,
1351
						),
1352
						'taxes' => array(
1353
							'description' => __( 'Line taxes.', 'woocommerce' ),
1354
							'type'        => 'array',
1355
							'context'     => array( 'view', 'edit' ),
1356
							'readonly'    => true,
1357
							'properties'  => array(
1358
								'id' => array(
1359
									'description' => __( 'Tax rate ID.', 'woocommerce' ),
1360
									'type'        => 'integer',
1361
									'context'     => array( 'view', 'edit' ),
1362
									'readonly'    => true,
1363
								),
1364
								'total' => array(
1365
									'description' => __( 'Tax total.', 'woocommerce' ),
1366
									'type'        => 'string',
1367
									'context'     => array( 'view', 'edit' ),
1368
									'readonly'    => true,
1369
								),
1370
							),
1371
						),
1372
						'meta_data' => array(
1373
							'description' => __( 'Order item meta data.', 'woocommerce' ),
1374
							'type'        => 'array',
1375
							'context'     => array( 'view', 'edit' ),
1376
							'properties'  => array(
1377
								'key' => array(
1378
									'description' => __( 'Meta key.', 'woocommerce' ),
1379
									'type'        => 'string',
1380
									'context'     => array( 'view', 'edit' ),
1381
								),
1382
								'value' => array(
1383
									'description' => __( 'Meta value.', 'woocommerce' ),
1384
									'type'        => 'string',
1385
									'context'     => array( 'view', 'edit' ),
1386
								),
1387
								'meta_id' => array(
1388
									'description' => __( 'Meta ID.', 'woocommerce' ),
1389
									'type'        => 'int',
1390
									'context'     => array( 'view', 'edit' ),
1391
									'readonly'    => true,
1392
								),
1393
							),
1394
						),
1395
					),
1396
				),
1397
				'fee_lines' => array(
1398
					'description' => __( 'Fee lines data.', 'woocommerce' ),
1399
					'type'        => 'array',
1400
					'context'     => array( 'view', 'edit' ),
1401
					'properties'  => array(
1402
						'id' => array(
1403
							'description' => __( 'Item ID.', 'woocommerce' ),
1404
							'type'        => 'integer',
1405
							'context'     => array( 'view', 'edit' ),
1406
							'readonly'    => true,
1407
						),
1408
						'name' => array(
1409
							'description' => __( 'Fee name.', 'woocommerce' ),
1410
							'type'        => 'string',
1411
							'context'     => array( 'view', 'edit' ),
1412
						),
1413
						'tax_class' => array(
1414
							'description' => __( 'Tax class of fee.', 'woocommerce' ),
1415
							'type'        => 'string',
1416
							'context'     => array( 'view', 'edit' ),
1417
						),
1418
						'tax_status' => array(
1419
							'description' => __( 'Tax status of fee.', 'woocommerce' ),
1420
							'type'        => 'string',
1421
							'context'     => array( 'view', 'edit' ),
1422
							'enum'        => array( 'taxable', 'none' ),
1423
						),
1424
						'total' => array(
1425
							'description' => __( 'Line total (after discounts).', 'woocommerce' ),
1426
							'type'        => 'string',
1427
							'context'     => array( 'view', 'edit' ),
1428
						),
1429
						'total_tax' => array(
1430
							'description' => __( 'Line total tax (after discounts).', 'woocommerce' ),
1431
							'type'        => 'string',
1432
							'context'     => array( 'view', 'edit' ),
1433
							'readonly'    => true,
1434
						),
1435
						'taxes' => array(
1436
							'description' => __( 'Line taxes.', 'woocommerce' ),
1437
							'type'        => 'array',
1438
							'context'     => array( 'view', 'edit' ),
1439
							'readonly'    => true,
1440
							'properties'  => array(
1441
								'id' => array(
1442
									'description' => __( 'Tax rate ID.', 'woocommerce' ),
1443
									'type'        => 'integer',
1444
									'context'     => array( 'view', 'edit' ),
1445
									'readonly'    => true,
1446
								),
1447
								'total' => array(
1448
									'description' => __( 'Tax total.', 'woocommerce' ),
1449
									'type'        => 'string',
1450
									'context'     => array( 'view', 'edit' ),
1451
									'readonly'    => true,
1452
								),
1453
								'subtotal' => array(
1454
									'description' => __( 'Tax subtotal.', 'woocommerce' ),
1455
									'type'        => 'string',
1456
									'context'     => array( 'view', 'edit' ),
1457
									'readonly'    => true,
1458
								),
1459
							),
1460
						),
1461
						'meta_data' => array(
1462
							'description' => __( 'Order item meta data.', 'woocommerce' ),
1463
							'type'        => 'array',
1464
							'context'     => array( 'view', 'edit' ),
1465
							'properties'  => array(
1466
								'key' => array(
1467
									'description' => __( 'Meta key.', 'woocommerce' ),
1468
									'type'        => 'string',
1469
									'context'     => array( 'view', 'edit' ),
1470
								),
1471
								'value' => array(
1472
									'description' => __( 'Meta value.', 'woocommerce' ),
1473
									'type'        => 'string',
1474
									'context'     => array( 'view', 'edit' ),
1475
								),
1476
								'meta_id' => array(
1477
									'description' => __( 'Meta ID.', 'woocommerce' ),
1478
									'type'        => 'int',
1479
									'context'     => array( 'view', 'edit' ),
1480
									'readonly'    => true,
1481
								),
1482
							),
1483
						),
1484
					),
1485
				),
1486
				'coupon_lines' => array(
1487
					'description' => __( 'Coupons line data.', 'woocommerce' ),
1488
					'type'        => 'array',
1489
					'context'     => array( 'view', 'edit' ),
1490
					'properties'  => array(
1491
						'id' => array(
1492
							'description' => __( 'Item ID.', 'woocommerce' ),
1493
							'type'        => 'integer',
1494
							'context'     => array( 'view', 'edit' ),
1495
							'readonly'    => true,
1496
						),
1497
						'code' => array(
1498
							'description' => __( 'Coupon code.', 'woocommerce' ),
1499
							'type'        => 'string',
1500
							'context'     => array( 'view', 'edit' ),
1501
						),
1502
						'discount' => array(
1503
							'description' => __( 'Discount total.', 'woocommerce' ),
1504
							'type'        => 'string',
1505
							'context'     => array( 'view', 'edit' ),
1506
						),
1507
						'discount_tax' => array(
1508
							'description' => __( 'Discount total tax.', 'woocommerce' ),
1509
							'type'        => 'string',
1510
							'context'     => array( 'view', 'edit' ),
1511
							'readonly'    => true,
1512
						),
1513
						'meta_data' => array(
1514
							'description' => __( 'Order item meta data.', 'woocommerce' ),
1515
							'type'        => 'array',
1516
							'context'     => array( 'view', 'edit' ),
1517
							'properties'  => array(
1518
								'key' => array(
1519
									'description' => __( 'Meta key.', 'woocommerce' ),
1520
									'type'        => 'string',
1521
									'context'     => array( 'view', 'edit' ),
1522
								),
1523
								'value' => array(
1524
									'description' => __( 'Meta value.', 'woocommerce' ),
1525
									'type'        => 'string',
1526
									'context'     => array( 'view', 'edit' ),
1527
								),
1528
								'meta_id' => array(
1529
									'description' => __( 'Meta ID.', 'woocommerce' ),
1530
									'type'        => 'int',
1531
									'context'     => array( 'view', 'edit' ),
1532
									'readonly'    => true,
1533
								),
1534
							),
1535
						),
1536
					),
1537
				),
1538
				'refunds' => array(
1539
					'description' => __( 'List of refunds.', 'woocommerce' ),
1540
					'type'        => 'array',
1541
					'context'     => array( 'view', 'edit' ),
1542
					'readonly'    => true,
1543
					'properties'  => array(
1544
						'id' => array(
1545
							'description' => __( 'Refund ID.', 'woocommerce' ),
1546
							'type'        => 'integer',
1547
							'context'     => array( 'view', 'edit' ),
1548
							'readonly'    => true,
1549
						),
1550
						'reason' => array(
1551
							'description' => __( 'Refund reason.', 'woocommerce' ),
1552
							'type'        => 'string',
1553
							'context'     => array( 'view', 'edit' ),
1554
							'readonly'    => true,
1555
						),
1556
						'total' => array(
1557
							'description' => __( 'Refund total.', 'woocommerce' ),
1558
							'type'        => 'string',
1559
							'context'     => array( 'view', 'edit' ),
1560
							'readonly'    => true,
1561
						),
1562
					),
1563
				),
1564
				'set_paid' => array(
1565
					'description' => __( 'Define if the order is paid. It will set the status to processing and reduce stock items.', 'woocommerce' ),
1566
					'type'        => 'boolean',
1567
					'default'     => false,
1568
					'context'     => array( 'edit' ),
1569
				),
1570
			),
1571
		);
1572
1573
		return $this->add_additional_fields_schema( $schema );
1574
	}
1575
1576
	/**
1577
	 * Get the query params for collections.
1578
	 *
1579
	 * @return array
1580
	 */
1581
	public function get_collection_params() {
1582
		$params = parent::get_collection_params();
1583
1584
		$params['status'] = array(
1585
			'default'           => 'any',
1586
			'description'       => __( 'Limit result set to orders assigned a specific status.', 'woocommerce' ),
1587
			'type'              => 'string',
1588
			'enum'              => array_merge( array( 'any' ), $this->get_order_statuses() ),
1589
			'sanitize_callback' => 'sanitize_key',
1590
			'validate_callback' => 'rest_validate_request_arg',
1591
		);
1592
		$params['customer'] = array(
1593
			'description'       => __( 'Limit result set to orders assigned a specific customer.', 'woocommerce' ),
1594
			'type'              => 'integer',
1595
			'sanitize_callback' => 'absint',
1596
			'validate_callback' => 'rest_validate_request_arg',
1597
		);
1598
		$params['product'] = array(
1599
			'description'       => __( 'Limit result set to orders assigned a specific product.', 'woocommerce' ),
1600
			'type'              => 'integer',
1601
			'sanitize_callback' => 'absint',
1602
			'validate_callback' => 'rest_validate_request_arg',
1603
		);
1604
		$params['dp'] = array(
1605
			'default'           => 2,
1606
			'description'       => __( 'Number of decimal points to use in each resource.', 'woocommerce' ),
1607
			'type'              => 'integer',
1608
			'sanitize_callback' => 'absint',
1609
			'validate_callback' => 'rest_validate_request_arg',
1610
		);
1611
1612
		return $params;
1613
	}
1614
}
1615