WC_POS_APIv2_Orders

POS Orders Class duck punches the WC REST API.

Defined (1)

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

/includes/apiv2/class-wc-pos-orders.php  
  1. class WC_POS_APIv2_Orders extends WC_POS_APIv2_Abstract { 
  2.  
  3. /** 
  4. * Constructor 
  5. */ 
  6. public function __construct() { 
  7. add_filter( 'woocommerce_rest_pre_insert_shop_order_object', array( $this, 'pre_insert_shop_order_object' ), 10, 3 ); 
  8. add_filter( 'woocommerce_rest_prepare_shop_order_object', array( $this, 'prepare_shop_order_object' ), 10, 3 ); 
  9. add_action( 'woocommerce_rest_set_order_item', array( $this, 'rest_set_order_item' ), 10, 2 ); 
  10.  
  11. $this->register_additional_fields(); 
  12. $this->unregister_emails(); 
  13.  
  14.  
  15. /** 
  16. * Additional fields for POS 
  17. */ 
  18. public function register_additional_fields() { 
  19.  
  20. // add cashier field 
  21. register_rest_field( 'shop_order',  
  22. 'cashier',  
  23. array( 
  24. 'get_callback' => array( $this , 'get_cashier' ),  
  25. 'update_callback' => array( $this , 'update_cashier' ),  
  26. 'schema' => null,  
  27. ); 
  28.  
  29. // add payment_details field 
  30. register_rest_field( 'shop_order',  
  31. 'payment_details',  
  32. array( 
  33. 'get_callback' => array( $this , 'get_payment_details' ),  
  34. 'update_callback' => array( $this , 'update_payment_details' ),  
  35. 'schema' => null,  
  36. ); 
  37.  
  38.  
  39.  
  40. /** 
  41. * Retrieve cashier info for the API response 
  42. * @param $response 
  43. * @return mixed|void 
  44. */ 
  45. public function get_cashier( $response ) { 
  46. $id = $response['id']; 
  47.  
  48. if ( !$cashier_id = get_post_meta( $id, '_pos_user', true ) ) { 
  49. return; 
  50.  
  51. $first_name = get_post_meta( $id, '_pos_user_first_name', true ); 
  52. $last_name = get_post_meta( $id, '_pos_user_last_name', true ); 
  53. if ( !$first_name && !$last_name && $user_info = get_userdata( $cashier_id ) ) { 
  54. $first_name = $user_info->first_name; 
  55. $last_name = $user_info->last_name; 
  56.  
  57. $cashier = array( 
  58. 'id' => $cashier_id,  
  59. 'first_name' => $first_name,  
  60. 'last_name' => $last_name 
  61. ); 
  62.  
  63. return apply_filters( 'woocommerce_pos_order_response_cashier', $cashier, $response ); 
  64.  
  65.  
  66. /** 
  67. * Store cashier data 
  68. * @param $cashier From the POS request body 
  69. * @param $order 
  70. */ 
  71. public function update_cashier( $cashier, $order ) { 
  72. $id = $order->get_id(); 
  73. $current_user = wp_get_current_user(); 
  74. update_post_meta( $id, '_pos', 1 ); 
  75. update_post_meta( $id, '_pos_user', $current_user->ID ); 
  76. update_post_meta( $id, '_pos_user_name', $current_user->user_firstname . ' ' . $current_user->user_lastname ); 
  77.  
  78.  
  79. /** 
  80. * Retrieve payment info for the API response 
  81. * @param $response 
  82. * @return mixed|void 
  83. */ 
  84. public function get_payment_details( $response ) { 
  85. $id = $response['id']; 
  86. $payment = array(); 
  87.  
  88. $payment['result'] = get_post_meta( $id, '_pos_payment_result', true ); 
  89. $payment['message'] = get_post_meta( $id, '_pos_payment_message', true ); 
  90. $payment['redirect'] = get_post_meta( $id, '_pos_payment_redirect', true ); 
  91.  
  92. $payment['method_id'] = isset( $response['payment_method'] ) ? $response['payment_method'] : ''; 
  93. $payment['method_title'] = isset( $response['payment_method_title'] ) ? $response['payment_method_title'] : ''; 
  94.  
  95. $payment['paid'] = $response['date_completed'] && $response['date_paid']; 
  96.  
  97. if( isset( $payment['method_id'] ) && $payment['method_id'] == 'pos_cash' ) { 
  98. $payment = WC_POS_Gateways_Cash::payment_details( $payment, $id ); 
  99.  
  100. return apply_filters( 'woocommerce_pos_order_response_payment_details', $payment, $response ); 
  101.  
  102.  
  103. /** 
  104. * Process payment and store result 
  105. * @param $payment_details From the POS request body 
  106. * @param $order 
  107. */ 
  108. public function update_payment_details( $payment_details, $order ) { 
  109.  
  110. // payment method 
  111. $payment_method = $order->get_payment_method(); 
  112.  
  113. // some gateways check if a user is signed in, so let's switch to customer 
  114. $logged_in_user = get_current_user_id(); 
  115. $customer_id = $order->get_customer_id(); 
  116. wp_set_current_user( $customer_id ); 
  117.  
  118. // load the gateways & process payment 
  119. add_filter('option_woocommerce_'. $payment_method .'_settings', array($this, 'force_enable_gateway')); 
  120. $settings = WC_POS_Admin_Settings_Checkout::get_instance(); 
  121. $gateways = $settings->load_enabled_gateways(); 
  122.  
  123. // process payment 
  124. do_action( 'woocommerce_pos_process_payment', $payment_details, $order); 
  125. $response = $gateways[ $payment_method ]->process_payment( $order->get_id() ); 
  126.  
  127. if(isset($response['result']) && $response['result'] == 'success') { 
  128.  
  129. $this->payment_success($payment_method, $order, $response); 
  130.  
  131. if( ! get_post_meta( $order->get_id(), '_pos_payment_redirect' ) ) { 
  132. $order->set_date_paid( current_time( 'timestamp' ) ); 
  133. $order->set_date_completed( current_time( 'timestamp' ) ); 
  134. $message = __('POS Transaction completed.', 'woocommerce-pos'); 
  135. $order->update_status( wc_pos_get_option( 'checkout', 'order_status' ), $message ); 
  136.  
  137. } else { 
  138.  
  139. $this->payment_failure($payment_method, $order, $response); 
  140. $order->update_status( 'wc-failed', __( 'There was an error processing the payment', 'woocommerce-pos') ); 
  141.  
  142.  
  143. // switch back to logged in user 
  144. wp_set_current_user( $logged_in_user ); 
  145.  
  146. // clear any payment gateway messages 
  147. wc_clear_notices(); 
  148.  
  149.  
  150. /** 
  151. * @param $order 
  152. * @param $request 
  153. * @param $creating 
  154. */ 
  155. public function pre_insert_shop_order_object( $order, $request, $creating ) { 
  156.  
  157. /** 
  158. * Transpose legacy api data to WC API v2 
  159. */ 
  160. if( isset($request['note']) ) { 
  161. $order->set_customer_note($request['note']); 
  162.  
  163. if( isset($request['payment_details']) ) { 
  164. $payment_details = $request['payment_details']; 
  165. $order->set_payment_method( isset($payment_details['method_id']) ? $payment_details['method_id'] : '' ); 
  166. $order->set_payment_method_title( isset($payment_details['method_title']) ? $payment_details['method_title'] : '' ); 
  167.  
  168. if( isset($request['customer']) && isset($request['customer']['billing_address']) ) { 
  169. $order->set_address( $request['customer']['billing_address'], 'billing' ); 
  170.  
  171. if( isset($request['customer']) && isset($request['customer']['shipping_address']) ) { 
  172. $order->set_address( $request['customer']['shipping_address'], 'shipping' ); 
  173.  
  174. // additional fields are required as part of request 
  175. $request['cashier'] = ''; 
  176.  
  177. // calculate taxes (trust the POS) 
  178. add_filter( 'wc_tax_enabled', '__return_false' ); 
  179. $order->save(); 
  180. $order->update_taxes(); 
  181. $order->calculate_totals(); 
  182.  
  183. return $order; 
  184.  
  185.  
  186. /** 
  187. * @param $response 
  188. * @param $order 
  189. * @param $request 
  190. * @return 
  191. */ 
  192. public function prepare_shop_order_object( $response, $order, $request ) { 
  193. $data = $response->get_data(); 
  194.  
  195. /** 
  196. * Legacy API Compatibiility 
  197. * duplicate props for receipt templates 
  198. */ 
  199. if($data['customer_note']) { 
  200. $data['note'] = $data['customer_note']; 
  201.  
  202. if($data['discount_total']) { 
  203. $data['total_discount'] = $data['discount_total']; 
  204.  
  205. if($data['shipping_total']) { 
  206. $data['total_shipping'] = $data['shipping_total']; 
  207.  
  208. if($data['number']) { 
  209. $data['order_number'] = $data['number']; 
  210.  
  211. if($data['date_modified']) { 
  212. $data['updated_at'] = $data['date_modified']; 
  213.  
  214. // customer 
  215. if( $data['billing'] ) { 
  216. $data['billing_address'] = $data['billing']; 
  217.  
  218. if( $data['shipping'] ) { 
  219. $data['shipping_address'] = $data['shipping']; 
  220.  
  221. if( $data['date_created'] ) { 
  222. $data['created_at'] = $data['date_created']; 
  223.  
  224. $response->set_data($data); 
  225. return $response; 
  226.  
  227.  
  228. /** 
  229. * @param $item 
  230. * @param $posted 
  231. */ 
  232. public function rest_set_order_item( $item, $posted ) { 
  233. $tax_status = isset($posted['taxable']) && $posted['taxable'] ? 'taxable' : 'none'; 
  234.  
  235. if( $tax_status == 'taxable' ) { 
  236. $total = array(); 
  237. $subtotal = array(); 
  238.  
  239. if(isset($posted['tax']) && is_array($posted['tax'])): foreach($posted['tax'] as $rate_id => $tax): 
  240. if(is_array($tax)) { 
  241. $total[$rate_id] = isset($tax['total']) ? $tax['total'] : 0; 
  242. $subtotal[$rate_id] = isset($tax['subtotal']) ? $tax['subtotal'] : 0; 
  243. endforeach; endif; 
  244.  
  245. if( get_class($item) == 'WC_Order_Item_Product' ) { 
  246. $item->set_taxes( array( 'total' => $total, 'subtotal' => $subtotal ) ); 
  247. } else { 
  248. $item->set_taxes( array( 'total' => $total ) ); 
  249.  
  250.  
  251. /** 
  252. * Some gateways will check if enabled 
  253. * @param $data 
  254. * @return mixed 
  255. */ 
  256. public function force_enable_gateway($data) { 
  257. if(isset($data['enabled'])) { 
  258. $data['enabled'] = 'yes'; 
  259. return $data; 
  260.  
  261.  
  262. /** 
  263. * @param $gateway_id 
  264. * @param $order 
  265. * @param $response 
  266. */ 
  267. private function payment_success($gateway_id, $order, $response) { 
  268.  
  269. // capture any instructions 
  270. ob_start(); 
  271. do_action( 'woocommerce_thankyou_' . $gateway_id, $order->get_id() ); 
  272. $response['messages'] = ob_get_contents(); 
  273. ob_end_clean(); 
  274.  
  275. // redirect 
  276. if( isset($response['redirect']) ) { 
  277. $response['messages'] = $this->payment_redirect($gateway_id, $order, $response); 
  278.  
  279. update_post_meta( $order->get_id(), '_pos_payment_result', 'success' ); 
  280. update_post_meta( $order->get_id(), '_pos_payment_message', $response['messages'] ); 
  281.  
  282.  
  283. /** 
  284. * @param $gateway_id 
  285. * @param $order 
  286. * @param $response 
  287. */ 
  288. private function payment_failure($gateway_id, $order, $response) { 
  289. $message = isset($response['messages']) ? $response['messages'] : wc_get_notices( 'error' ); 
  290.  
  291. // if messages empty give generic response 
  292. if(empty($message)) { 
  293. $response['messages'] = __( 'There was an error processing the payment', 'woocommerce-pos'); 
  294.  
  295. update_post_meta( $order->get_id(), '_pos_payment_result', 'failure' ); 
  296. update_post_meta( $order->get_id(), '_pos_payment_message', $response['messages'] ); 
  297.  
  298. /** 
  299. * @param $gateway_id 
  300. * @param $order 
  301. * @param $response 
  302. * @return string 
  303. */ 
  304. private function payment_redirect($gateway_id, $order, $response) { 
  305. $message = $response['messages']; 
  306.  
  307. // compare url fragments 
  308. $success_url = wc_get_endpoint_url( 'order-received', $order->get_id(), get_permalink( wc_get_page_id( 'checkout' ) ) ); 
  309. $success = wp_parse_args( parse_url( $success_url ), array( 'host' => '', 'path' => '' )); 
  310. $redirect = wp_parse_args( parse_url( $response['redirect'] ), array( 'host' => '', 'path' => '' )); 
  311.  
  312. $offsite = $success['path'] !== $redirect['path'] && $response['messages'] == ''; 
  313.  
  314. if($offsite) { 
  315. update_post_meta( $order->get_id(), '_pos_payment_redirect', $response['redirect'] ); 
  316. $message = __('You are now being redirected offsite to complete the payment. ', 'woocommerce-pos'); 
  317. $message .= sprintf( __('<a href="%s">Click here</a> if you are not redirected automatically. ', 'woocommerce-pos'), $response['redirect'] ); 
  318.  
  319. return $message; 
  320.  
  321.  
  322. /** 
  323. * Adds support for custom address fields 
  324. * @param $address 
  325. * @param $order 
  326. * @param string $type 
  327. * @return array 
  328. */ 
  329. private function filter_address( $address, $order, $type = 'billing' ) { 
  330. $fields = apply_filters('woocommerce_admin_'.$type.'_fields', false); 
  331. if( $fields ) { 
  332. $address = array(); 
  333. foreach($fields as $key => $value) { 
  334. $address[$key] = $order->{$type.'_'.$key}; 
  335. return $address; 
  336.  
  337.  
  338. /** 
  339. * Get customer details 
  340. * - mirrors woocommerce/includes/class-wc-ajax.php->get_customer_details() 
  341. * @param $user_id 
  342. * @param $type_to_load 
  343. * @return mixed|void 
  344. */ 
  345. private function get_customer_details( $user_id, $type_to_load ) { 
  346. $customer_data = array( 
  347. $type_to_load . '_first_name' => get_user_meta( $user_id, $type_to_load . '_first_name', true ),  
  348. $type_to_load . '_last_name' => get_user_meta( $user_id, $type_to_load . '_last_name', true ),  
  349. $type_to_load . '_company' => get_user_meta( $user_id, $type_to_load . '_company', true ),  
  350. $type_to_load . '_address_1' => get_user_meta( $user_id, $type_to_load . '_address_1', true ),  
  351. $type_to_load . '_address_2' => get_user_meta( $user_id, $type_to_load . '_address_2', true ),  
  352. $type_to_load . '_city' => get_user_meta( $user_id, $type_to_load . '_city', true ),  
  353. $type_to_load . '_postcode' => get_user_meta( $user_id, $type_to_load . '_postcode', true ),  
  354. $type_to_load . '_country' => get_user_meta( $user_id, $type_to_load . '_country', true ),  
  355. $type_to_load . '_state' => get_user_meta( $user_id, $type_to_load . '_state', true ),  
  356. $type_to_load . '_email' => get_user_meta( $user_id, $type_to_load . '_email', true ),  
  357. $type_to_load . '_phone' => get_user_meta( $user_id, $type_to_load . '_phone', true ),  
  358. ); 
  359. $customer_data = apply_filters( 'woocommerce_found_customer_details', $customer_data, $user_id, $type_to_load ); 
  360.  
  361. // remove billing_ or shipping_ prefix for WC REST API 
  362. $data = array(); 
  363. foreach( $customer_data as $key => $value ): if($value): 
  364. $key = str_replace( $type_to_load.'_', '', $key ); 
  365. $data[$key] = $value; 
  366. endif; endforeach; 
  367. return $data; 
  368.  
  369.  
  370. /** 
  371. * Returns array of all order ids 
  372. * optionally return ids updated_at_min 
  373. * @param $date_modified 
  374. * @return array 
  375. */ 
  376. public function get_ids($date_modified) { 
  377. $args = array( 
  378. 'post_type' => array('shop_order'),  
  379. 'post_status' => array('any'),  
  380. 'posts_per_page'=> -1,  
  381. 'fields' => 'ids' 
  382. ); 
  383.  
  384. if($date_modified) { 
  385. $args['date_query'][] = array( 
  386. 'column' => 'post_modified_gmt',  
  387. 'after' => $date_modified,  
  388. 'inclusive' => false 
  389. ); 
  390.  
  391. $query = new WP_Query( $args ); 
  392. return array_map( 'intval', $query->posts ); 
  393.  
  394.  
  395. /** 
  396. * Allow users to unregister WC emails 
  397. */ 
  398. public function unregister_emails( ) { 
  399. $wc_emails = WC()->mailer(); 
  400.  
  401. if( get_class($wc_emails) !== 'WC_Emails' ) { 
  402. return; 
  403.  
  404. if( ! wc_pos_get_option( 'checkout', 'customer_emails' ) ) { 
  405. $this->remove_customer_emails($wc_emails); 
  406.  
  407. if( ! wc_pos_get_option( 'checkout', 'admin_emails' ) ) { 
  408. $this->remove_admin_emails($wc_emails); 
  409.  
  410.  
  411. /** 
  412. * Unhook customer emails 
  413. * @param WC_Emails $wc_emails 
  414. * @internal param $emails 
  415. */ 
  416. public function remove_customer_emails( WC_Emails $wc_emails ) { 
  417.  
  418. remove_action( 
  419. 'woocommerce_order_status_pending_to_processing_notification',  
  420. array( 
  421. $wc_emails->emails['WC_Email_Customer_Processing_Order'],  
  422. 'trigger' 
  423. ); 
  424. remove_action( 
  425. 'woocommerce_order_status_pending_to_on-hold_notification',  
  426. array( 
  427. $wc_emails->emails['WC_Email_Customer_Processing_Order'],  
  428. 'trigger' 
  429. ); 
  430. remove_action( 
  431. 'woocommerce_order_status_completed_notification',  
  432. array( 
  433. $wc_emails->emails['WC_Email_Customer_Completed_Order'],  
  434. 'trigger' 
  435. ); 
  436.  
  437.  
  438. /** 
  439. * Unhook admin emails 
  440. * @param WC_Emails $wc_emails 
  441. * @return array 
  442. */ 
  443. private function remove_admin_emails( WC_Emails $wc_emails ) { 
  444. // send 'woocommerce_low_stock_notification' 
  445. // send 'woocommerce_no_stock_notification' 
  446. // send 'woocommerce_product_on_backorder_notification' 
  447.  
  448. remove_action( 
  449. 'woocommerce_order_status_pending_to_processing_notification',  
  450. array( 
  451. $wc_emails->emails['WC_Email_New_Order'],  
  452. 'trigger' 
  453. ); 
  454. remove_action( 
  455. 'woocommerce_order_status_pending_to_completed_notification',  
  456. array( 
  457. $wc_emails->emails['WC_Email_New_Order'],  
  458. 'trigger' 
  459. ); 
  460. remove_action( 
  461. 'woocommerce_order_status_pending_to_on-hold_notification',  
  462. array( 
  463. $wc_emails->emails['WC_Email_New_Order'],  
  464. 'trigger' 
  465. ); 
  466. remove_action( 
  467. 'woocommerce_order_status_failed_to_processing_notification',  
  468. array( 
  469. $wc_emails->emails['WC_Email_New_Order'],  
  470. 'trigger' 
  471. ); 
  472. remove_action( 
  473. 'woocommerce_order_status_failed_to_completed_notification',  
  474. array( 
  475. $wc_emails->emails['WC_Email_New_Order'],  
  476. 'trigger' 
  477. ); 
  478. remove_action( 
  479. 'woocommerce_order_status_failed_to_on-hold_notification',  
  480. array( 
  481. $wc_emails->emails['WC_Email_New_Order'],  
  482. 'trigger' 
  483. ); 
  484.  
  485.