WC_REST_Order_Refunds_V1_Controller

REST API Order Refunds controller class.

Defined (1)

The class is defined in the following location(s).

/includes/api/v1/class-wc-rest-order-refunds-controller.php  
  1. class WC_REST_Order_Refunds_V1_Controller extends WC_REST_Orders_V1_Controller { 
  2.  
  3. /** 
  4. * Endpoint namespace. 
  5. * @var string 
  6. */ 
  7. protected $namespace = 'wc/v1'; 
  8.  
  9. /** 
  10. * Route base. 
  11. * @var string 
  12. */ 
  13. protected $rest_base = 'orders/(?P<order_id>[\d]+)/refunds'; 
  14.  
  15. /** 
  16. * Post type. 
  17. * @var string 
  18. */ 
  19. protected $post_type = 'shop_order_refund'; 
  20.  
  21. /** 
  22. * Order refunds actions. 
  23. */ 
  24. public function __construct() { 
  25. add_filter( "woocommerce_rest_{$this->post_type}_trashable", '__return_false' ); 
  26. add_filter( "woocommerce_rest_{$this->post_type}_query", array( $this, 'query_args' ), 10, 2 ); 
  27.  
  28. /** 
  29. * Register the routes for order refunds. 
  30. */ 
  31. public function register_routes() { 
  32. register_rest_route( $this->namespace, '/' . $this->rest_base, array( 
  33. 'args' => array( 
  34. 'order_id' => array( 
  35. 'description' => __( 'The order ID.', 'woocommerce' ),  
  36. 'type' => 'integer',  
  37. ),  
  38. ),  
  39. array( 
  40. 'methods' => WP_REST_Server::READABLE,  
  41. 'callback' => array( $this, 'get_items' ),  
  42. 'permission_callback' => array( $this, 'get_items_permissions_check' ),  
  43. 'args' => $this->get_collection_params(),  
  44. ),  
  45. array( 
  46. 'methods' => WP_REST_Server::CREATABLE,  
  47. 'callback' => array( $this, 'create_item' ),  
  48. 'permission_callback' => array( $this, 'create_item_permissions_check' ),  
  49. 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::CREATABLE ),  
  50. ),  
  51. 'schema' => array( $this, 'get_public_item_schema' ),  
  52. ) ); 
  53.  
  54. register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<id>[\d]+)', array( 
  55. 'args' => array( 
  56. 'order_id' => array( 
  57. 'description' => __( 'The order ID.', 'woocommerce' ),  
  58. 'type' => 'integer',  
  59. ),  
  60. 'id' => array( 
  61. 'description' => __( 'Unique identifier for the resource.', 'woocommerce' ),  
  62. 'type' => 'integer',  
  63. ),  
  64. ),  
  65. array( 
  66. 'methods' => WP_REST_Server::READABLE,  
  67. 'callback' => array( $this, 'get_item' ),  
  68. 'permission_callback' => array( $this, 'get_item_permissions_check' ),  
  69. 'args' => array( 
  70. 'context' => $this->get_context_param( array( 'default' => 'view' ) ),  
  71. ),  
  72. ),  
  73. array( 
  74. 'methods' => WP_REST_Server::DELETABLE,  
  75. 'callback' => array( $this, 'delete_item' ),  
  76. 'permission_callback' => array( $this, 'delete_item_permissions_check' ),  
  77. 'args' => array( 
  78. 'force' => array( 
  79. 'default' => true,  
  80. 'type' => 'boolean',  
  81. 'description' => __( 'Required to be true, as resource does not support trashing.', 'woocommerce' ),  
  82. ),  
  83. ),  
  84. ),  
  85. 'schema' => array( $this, 'get_public_item_schema' ),  
  86. ) ); 
  87.  
  88. /** 
  89. * Prepare a single order refund output for response. 
  90. * @param WP_Post $post Post object. 
  91. * @param WP_REST_Request $request Request object. 
  92. * @return WP_REST_Response $data 
  93. */ 
  94. public function prepare_item_for_response( $post, $request ) { 
  95. $order = wc_get_order( (int) $request['order_id'] ); 
  96.  
  97. if ( ! $order ) { 
  98. return new WP_Error( 'woocommerce_rest_invalid_order_id', __( 'Invalid order ID.', 'woocommerce' ), 404 ); 
  99.  
  100. $refund = wc_get_order( $post ); 
  101.  
  102. if ( ! $refund || $refund->get_parent_id() !== $order->get_id() ) { 
  103. return new WP_Error( 'woocommerce_rest_invalid_order_refund_id', __( 'Invalid order refund ID.', 'woocommerce' ), 404 ); 
  104.  
  105. $dp = $request['dp']; 
  106.  
  107. $data = array( 
  108. 'id' => $refund->get_id(),  
  109. 'date_created' => wc_rest_prepare_date_response( $refund->get_date_created() ),  
  110. 'amount' => wc_format_decimal( $refund->get_amount(), $dp ),  
  111. 'reason' => $refund->get_reason(),  
  112. 'line_items' => array(),  
  113. ); 
  114.  
  115. // Add line items. 
  116. foreach ( $refund->get_items() as $item_id => $item ) { 
  117. $product = $refund->get_product_from_item( $item ); 
  118. $product_id = 0; 
  119. $variation_id = 0; 
  120. $product_sku = null; 
  121.  
  122. // Check if the product exists. 
  123. if ( is_object( $product ) ) { 
  124. $product_id = $item->get_product_id(); 
  125. $variation_id = $item->get_variation_id(); 
  126. $product_sku = $product->get_sku(); 
  127.  
  128. $meta = new WC_Order_Item_Meta( $item, $product ); 
  129.  
  130. $item_meta = array(); 
  131.  
  132. $hideprefix = 'true' === $request['all_item_meta'] ? null : '_'; 
  133.  
  134. foreach ( $meta->get_formatted( $hideprefix ) as $meta_key => $formatted_meta ) { 
  135. $item_meta[] = array( 
  136. 'key' => $formatted_meta['key'],  
  137. 'label' => $formatted_meta['label'],  
  138. 'value' => $formatted_meta['value'],  
  139. ); 
  140.  
  141. $line_item = array( 
  142. 'id' => $item_id,  
  143. 'name' => $item['name'],  
  144. 'sku' => $product_sku,  
  145. 'product_id' => (int) $product_id,  
  146. 'variation_id' => (int) $variation_id,  
  147. 'quantity' => wc_stock_amount( $item['qty'] ),  
  148. 'tax_class' => ! empty( $item['tax_class'] ) ? $item['tax_class'] : '',  
  149. 'price' => wc_format_decimal( $refund->get_item_total( $item, false, false ), $dp ),  
  150. 'subtotal' => wc_format_decimal( $refund->get_line_subtotal( $item, false, false ), $dp ),  
  151. 'subtotal_tax' => wc_format_decimal( $item['line_subtotal_tax'], $dp ),  
  152. 'total' => wc_format_decimal( $refund->get_line_total( $item, false, false ), $dp ),  
  153. 'total_tax' => wc_format_decimal( $item['line_tax'], $dp ),  
  154. 'taxes' => array(),  
  155. 'meta' => $item_meta,  
  156. ); 
  157.  
  158. $item_line_taxes = maybe_unserialize( $item['line_tax_data'] ); 
  159. if ( isset( $item_line_taxes['total'] ) ) { 
  160. $line_tax = array(); 
  161.  
  162. foreach ( $item_line_taxes['total'] as $tax_rate_id => $tax ) { 
  163. $line_tax[ $tax_rate_id ] = array( 
  164. 'id' => $tax_rate_id,  
  165. 'total' => $tax,  
  166. 'subtotal' => '',  
  167. ); 
  168.  
  169. foreach ( $item_line_taxes['subtotal'] as $tax_rate_id => $tax ) { 
  170. $line_tax[ $tax_rate_id ]['subtotal'] = $tax; 
  171.  
  172. $line_item['taxes'] = array_values( $line_tax ); 
  173.  
  174. $data['line_items'][] = $line_item; 
  175.  
  176. $context = ! empty( $request['context'] ) ? $request['context'] : 'view'; 
  177. $data = $this->add_additional_fields_to_object( $data, $request ); 
  178. $data = $this->filter_response_by_context( $data, $context ); 
  179.  
  180. // Wrap the data in a response object. 
  181. $response = rest_ensure_response( $data ); 
  182.  
  183. $response->add_links( $this->prepare_links( $refund, $request ) ); 
  184.  
  185. /** 
  186. * Filter the data for a response. 
  187. * The dynamic portion of the hook name, $this->post_type, refers to post_type of the post being 
  188. * prepared for the response. 
  189. * @param WP_REST_Response $response The response object. 
  190. * @param WP_Post $post Post object. 
  191. * @param WP_REST_Request $request Request object. 
  192. */ 
  193. return apply_filters( "woocommerce_rest_prepare_{$this->post_type}", $response, $post, $request ); 
  194.  
  195. /** 
  196. * Prepare links for the request. 
  197. * @param WC_Order_Refund $refund Comment object. 
  198. * @param WP_REST_Request $request Request object. 
  199. * @return array Links for the given order refund. 
  200. */ 
  201. protected function prepare_links( $refund, $request ) { 
  202. $order_id = $refund->get_parent_id(); 
  203. $base = str_replace( '(?P<order_id>[\d]+)', $order_id, $this->rest_base ); 
  204. $links = array( 
  205. 'self' => array( 
  206. 'href' => rest_url( sprintf( '/%s/%s/%d', $this->namespace, $base, $refund->get_id() ) ),  
  207. ),  
  208. 'collection' => array( 
  209. 'href' => rest_url( sprintf( '/%s/%s', $this->namespace, $base ) ),  
  210. ),  
  211. 'up' => array( 
  212. 'href' => rest_url( sprintf( '/%s/orders/%d', $this->namespace, $order_id ) ),  
  213. ),  
  214. ); 
  215.  
  216. return $links; 
  217.  
  218. /** 
  219. * Query args. 
  220. * @param array $args Request args. 
  221. * @param WP_REST_Request $request Request object. 
  222. * @return array 
  223. */ 
  224. public function query_args( $args, $request ) { 
  225. $args['post_status'] = array_keys( wc_get_order_statuses() ); 
  226. $args['post_parent__in'] = array( absint( $request['order_id'] ) ); 
  227.  
  228. return $args; 
  229.  
  230. /** 
  231. * Create a single item. 
  232. * @param WP_REST_Request $request Full details about the request. 
  233. * @return WP_Error|WP_REST_Response 
  234. */ 
  235. public function create_item( $request ) { 
  236. if ( ! empty( $request['id'] ) ) { 
  237. /** translators: %s: post type */ 
  238. return new WP_Error( "woocommerce_rest_{$this->post_type}_exists", sprintf( __( 'Cannot create existing %s.', 'woocommerce' ), $this->post_type ), array( 'status' => 400 ) ); 
  239.  
  240. $order_data = get_post( (int) $request['order_id'] ); 
  241.  
  242. if ( empty( $order_data ) ) { 
  243. return new WP_Error( 'woocommerce_rest_invalid_order', __( 'Order is invalid', 'woocommerce' ), 400 ); 
  244.  
  245. if ( 0 > $request['amount'] ) { 
  246. return new WP_Error( 'woocommerce_rest_invalid_order_refund', __( 'Refund amount must be greater than zero.', 'woocommerce' ), 400 ); 
  247.  
  248. // Create the refund. 
  249. $refund = wc_create_refund( array( 
  250. 'order_id' => $order_data->ID,  
  251. 'amount' => $request['amount'],  
  252. 'reason' => empty( $request['reason'] ) ? null : $request['reason'],  
  253. 'line_items' => $request['line_items'],  
  254. 'refund_payment' => is_bool( $request['api_refund'] ) ? $request['api_refund'] : true,  
  255. 'restock_items' => true,  
  256. ) ); 
  257.  
  258. if ( is_wp_error( $refund ) ) { 
  259. return new WP_Error( 'woocommerce_rest_cannot_create_order_refund', $refund->get_error_message(), 500 ); 
  260.  
  261. if ( ! $refund ) { 
  262. return new WP_Error( 'woocommerce_rest_cannot_create_order_refund', __( 'Cannot create order refund, please try again.', 'woocommerce' ), 500 ); 
  263.  
  264. $post = get_post( $refund->get_id() ); 
  265. $this->update_additional_fields_for_object( $post, $request ); 
  266.  
  267. /** 
  268. * Fires after a single item is created or updated via the REST API. 
  269. * @param WP_Post $post Post object. 
  270. * @param WP_REST_Request $request Request object. 
  271. * @param boolean $creating True when creating item, false when updating. 
  272. */ 
  273. do_action( "woocommerce_rest_insert_{$this->post_type}", $post, $request, true ); 
  274.  
  275. $request->set_param( 'context', 'edit' ); 
  276. $response = $this->prepare_item_for_response( $post, $request ); 
  277. $response = rest_ensure_response( $response ); 
  278. $response->set_status( 201 ); 
  279. $response->header( 'Location', rest_url( sprintf( '/%s/%s/%d', $this->namespace, $this->rest_base, $post->ID ) ) ); 
  280.  
  281. return $response; 
  282.  
  283. /** 
  284. * Get the Order's schema, conforming to JSON Schema. 
  285. * @return array 
  286. */ 
  287. public function get_item_schema() { 
  288. $schema = array( 
  289. '$schema' => 'http://json-schema.org/draft-04/schema#',  
  290. 'title' => $this->post_type,  
  291. 'type' => 'object',  
  292. 'properties' => array( 
  293. 'id' => array( 
  294. 'description' => __( 'Unique identifier for the resource.', 'woocommerce' ),  
  295. 'type' => 'integer',  
  296. 'context' => array( 'view', 'edit' ),  
  297. 'readonly' => true,  
  298. ),  
  299. 'date_created' => array( 
  300. 'description' => __( "The date the order refund was created, in the site's timezone.", 'woocommerce' ),  
  301. 'type' => 'date-time',  
  302. 'context' => array( 'view', 'edit' ),  
  303. 'readonly' => true,  
  304. ),  
  305. 'amount' => array( 
  306. 'description' => __( 'Refund amount.', 'woocommerce' ),  
  307. 'type' => 'string',  
  308. 'context' => array( 'view', 'edit' ),  
  309. ),  
  310. 'reason' => array( 
  311. 'description' => __( 'Reason for refund.', 'woocommerce' ),  
  312. 'type' => 'string',  
  313. 'context' => array( 'view', 'edit' ),  
  314. ),  
  315. 'line_items' => array( 
  316. 'description' => __( 'Line items data.', 'woocommerce' ),  
  317. 'type' => 'array',  
  318. 'context' => array( 'view', 'edit' ),  
  319. 'items' => array( 
  320. 'type' => 'object',  
  321. 'properties' => array( 
  322. 'id' => array( 
  323. 'description' => __( 'Item ID.', 'woocommerce' ),  
  324. 'type' => 'integer',  
  325. 'context' => array( 'view', 'edit' ),  
  326. 'readonly' => true,  
  327. ),  
  328. 'name' => array( 
  329. 'description' => __( 'Product name.', 'woocommerce' ),  
  330. 'type' => 'string',  
  331. 'context' => array( 'view', 'edit' ),  
  332. 'readonly' => true,  
  333. ),  
  334. 'sku' => array( 
  335. 'description' => __( 'Product SKU.', 'woocommerce' ),  
  336. 'type' => 'string',  
  337. 'context' => array( 'view', 'edit' ),  
  338. 'readonly' => true,  
  339. ),  
  340. 'product_id' => array( 
  341. 'description' => __( 'Product ID.', 'woocommerce' ),  
  342. 'type' => 'integer',  
  343. 'context' => array( 'view', 'edit' ),  
  344. ),  
  345. 'variation_id' => array( 
  346. 'description' => __( 'Variation ID, if applicable.', 'woocommerce' ),  
  347. 'type' => 'integer',  
  348. 'context' => array( 'view', 'edit' ),  
  349. ),  
  350. 'quantity' => array( 
  351. 'description' => __( 'Quantity ordered.', 'woocommerce' ),  
  352. 'type' => 'integer',  
  353. 'context' => array( 'view', 'edit' ),  
  354. ),  
  355. 'tax_class' => array( 
  356. 'description' => __( 'Tax class of product.', 'woocommerce' ),  
  357. 'type' => 'string',  
  358. 'context' => array( 'view', 'edit' ),  
  359. 'readonly' => true,  
  360. ),  
  361. 'price' => array( 
  362. 'description' => __( 'Product price.', 'woocommerce' ),  
  363. 'type' => 'string',  
  364. 'context' => array( 'view', 'edit' ),  
  365. 'readonly' => true,  
  366. ),  
  367. 'subtotal' => array( 
  368. 'description' => __( 'Line subtotal (before discounts).', 'woocommerce' ),  
  369. 'type' => 'string',  
  370. 'context' => array( 'view', 'edit' ),  
  371. ),  
  372. 'subtotal_tax' => array( 
  373. 'description' => __( 'Line subtotal tax (before discounts).', 'woocommerce' ),  
  374. 'type' => 'string',  
  375. 'context' => array( 'view', 'edit' ),  
  376. ),  
  377. 'total' => array( 
  378. 'description' => __( 'Line total (after discounts).', 'woocommerce' ),  
  379. 'type' => 'string',  
  380. 'context' => array( 'view', 'edit' ),  
  381. ),  
  382. 'total_tax' => array( 
  383. 'description' => __( 'Line total tax (after discounts).', 'woocommerce' ),  
  384. 'type' => 'string',  
  385. 'context' => array( 'view', 'edit' ),  
  386. ),  
  387. 'taxes' => array( 
  388. 'description' => __( 'Line taxes.', 'woocommerce' ),  
  389. 'type' => 'array',  
  390. 'context' => array( 'view', 'edit' ),  
  391. 'readonly' => true,  
  392. 'items' => array( 
  393. 'type' => 'object',  
  394. 'properties' => array( 
  395. 'id' => array( 
  396. 'description' => __( 'Tax rate ID.', 'woocommerce' ),  
  397. 'type' => 'integer',  
  398. 'context' => array( 'view', 'edit' ),  
  399. 'readonly' => true,  
  400. ),  
  401. 'total' => array( 
  402. 'description' => __( 'Tax total.', 'woocommerce' ),  
  403. 'type' => 'string',  
  404. 'context' => array( 'view', 'edit' ),  
  405. 'readonly' => true,  
  406. ),  
  407. 'subtotal' => array( 
  408. 'description' => __( 'Tax subtotal.', 'woocommerce' ),  
  409. 'type' => 'string',  
  410. 'context' => array( 'view', 'edit' ),  
  411. 'readonly' => true,  
  412. ),  
  413. ),  
  414. ),  
  415. ),  
  416. 'meta' => array( 
  417. 'description' => __( 'Line item meta data.', 'woocommerce' ),  
  418. 'type' => 'array',  
  419. 'context' => array( 'view', 'edit' ),  
  420. 'readonly' => true,  
  421. 'items' => array( 
  422. 'type' => 'object',  
  423. 'properties' => array( 
  424. 'key' => array( 
  425. 'description' => __( 'Meta key.', 'woocommerce' ),  
  426. 'type' => 'string',  
  427. 'context' => array( 'view', 'edit' ),  
  428. 'readonly' => true,  
  429. ),  
  430. 'label' => array( 
  431. 'description' => __( 'Meta label.', 'woocommerce' ),  
  432. 'type' => 'string',  
  433. 'context' => array( 'view', 'edit' ),  
  434. 'readonly' => true,  
  435. ),  
  436. 'value' => array( 
  437. 'description' => __( 'Meta value.', 'woocommerce' ),  
  438. 'type' => 'string',  
  439. 'context' => array( 'view', 'edit' ),  
  440. 'readonly' => true,  
  441. ),  
  442. ),  
  443. ),  
  444. ),  
  445. ),  
  446. ),  
  447. ),  
  448. ),  
  449. ); 
  450.  
  451. return $this->add_additional_fields_schema( $schema ); 
  452.  
  453. /** 
  454. * Get the query params for collections. 
  455. * @return array 
  456. */ 
  457. public function get_collection_params() { 
  458. $params = parent::get_collection_params(); 
  459.  
  460. $params['dp'] = array( 
  461. 'default' => 2,  
  462. 'description' => __( 'Number of decimal points to use in each resource.', 'woocommerce' ),  
  463. 'type' => 'integer',  
  464. 'sanitize_callback' => 'absint',  
  465. 'validate_callback' => 'rest_validate_request_arg',  
  466. ); 
  467.  
  468. return $params;