PluginProbe ʕ •ᴥ•ʔ
WooCommerce Square / 3.8.2
WooCommerce Square v3.8.2
5.5.0 5.4.3 5.4.2 5.4.1 5.4.0 trunk 1.0.25 1.0.26 1.0.27 1.0.28 1.0.29 1.0.30 1.0.31 1.0.32 1.0.33 1.0.34 1.0.35 1.0.36 1.0.37 1.0.38 2.0.0 2.0.1 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.0.8 2.1.0 2.1.1 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.2.0 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.3.0 2.3.1 2.3.2 2.3.3 2.3.4 2.4.0 2.4.1 2.5.0 2.5.1 2.5.2 2.5.3 2.6.0 2.7.0 2.8.0 2.9.0 2.9.1 3.0.0 3.0.1 3.0.2 3.0.3 3.1.0 3.2.0 3.3.0 3.4.0 3.4.1 3.4.2 3.5.0 3.6.0 3.6.1 3.7.0 3.7.1 3.8.0 3.8.1 3.8.2 3.8.3 3.9.0 4.0.0 4.1.0 4.2.0 4.2.1 4.2.2 4.2.3 4.3.0 4.3.1 4.3.2 4.4.0 4.4.1 4.4.2 4.5.0 4.5.1 4.5.2 4.6.0 4.6.1 4.6.2 4.6.3 4.6.4 4.7.0 4.7.1 4.7.2 4.7.3 4.7.4 4.8.0 4.8.1 4.8.2 4.8.3 4.8.4 4.8.5 4.8.6 4.8.7 4.8.8 4.9.0 4.9.1 4.9.2 4.9.3 4.9.4 4.9.5 4.9.6 4.9.7 4.9.8 4.9.9 5.0.0 5.0.1 5.1.0 5.1.1 5.1.2 5.2.0 5.3.0 5.3.1 5.3.2 5.3.3
woocommerce-square / includes / Handlers / Order.php
woocommerce-square / includes / Handlers Last commit date
Product 3 years ago Background_Job.php 3 years ago Category.php 3 years ago Connection.php 3 years ago Email.php 3 years ago Order.php 3 years ago Product.php 3 years ago Products.php 3 years ago Sync.php 3 years ago
Order.php
565 lines
1 <?php
2 /**
3 * WooCommerce Square
4 *
5 * This source file is subject to the GNU General Public License v3.0
6 * that is bundled with this package in the file license.txt.
7 * It is also available through the world-wide-web at this URL:
8 * http://www.gnu.org/licenses/gpl-3.0.html GNU General Public License v3.0 or later
9 * If you did not receive a copy of the license and are unable to
10 * obtain it through the world-wide-web, please send an email
11 * to license@woocommerce.com so we can send you a copy immediately.
12 *
13 * DISCLAIMER
14 *
15 * Do not edit or add to this file if you wish to upgrade WooCommerce Square to newer
16 * versions in the future. If you wish to customize WooCommerce Square for your
17 * needs please refer to https://docs.woocommerce.com/document/woocommerce-square/
18 *
19 * @author WooCommerce
20 * @copyright Copyright: (c) 2019, Automattic, Inc.
21 * @license http://www.gnu.org/licenses/gpl-3.0.html GNU General Public License v3.0 or later
22 */
23
24 namespace WooCommerce\Square\Handlers;
25
26 use WooCommerce\Square\Plugin;
27 use WooCommerce\Square\Handlers\Product;
28
29 defined( 'ABSPATH' ) || exit;
30
31 /**
32 * Order handler class.
33 *
34 * @since 2.0.0
35 */
36 class Order {
37
38 /**
39 * Array of previous stock values.
40 *
41 * @var array
42 */
43 private $previous_stock = array();
44
45 /**
46 * Array of product IDs that have been scheduled for sync in this request.
47 *
48 * @var array
49 */
50 private $products_to_sync = array();
51
52
53 /**
54 * Sets up Square order handler.
55 *
56 * @since 2.0.0
57 */
58 public function __construct() {
59
60 // remove Square variation IDs from order item meta
61 add_action( 'woocommerce_hidden_order_itemmeta', array( $this, 'hide_square_order_item_meta' ) );
62
63 // ADD hooks for stock syncs based on changes from orders not from this gateway
64 add_action( 'woocommerce_checkout_order_processed', array( $this, 'maybe_sync_stock_for_order_via_other_gateway' ), 10, 3 );
65
66 // Add specific hook for paypal IPN callback
67 add_action( 'valid-paypal-standard-ipn-request', array( $this, 'maybe_sync_stock_for_order_via_paypal' ), 10, 1 );
68
69 // ADD hooks to listen to refunds on orders from other gateways.
70 add_action( 'woocommerce_order_refunded', array( $this, 'maybe_sync_stock_for_refund_from_other_gateway' ), 10, 2 );
71
72 // Add gift card order item to the order edit screen.
73 add_action( 'woocommerce_admin_order_items_after_fees', array( $this, 'add_admin_order_items' ) );
74
75 // Include gift card information in payment method info.
76 add_filter( 'woocommerce_order_get_payment_method_title', array( $this, 'filter_payment_method_title' ), 10, 2 );
77 add_filter( 'woocommerce_gateway_title', array( $this, 'filter_gateway_title' ), 10, 2 );
78 }
79
80
81 /**
82 * Ensures the Square order item meta is hidden.
83 *
84 * @since 2.0.0
85 *
86 * @param string[] $hidden the hidden order item meta
87 * @return string[] updated meta
88 */
89 public function hide_square_order_item_meta( $hidden ) {
90
91 $hidden[] = '_square_item_variation_id';
92
93 return $hidden;
94 }
95
96 /**
97 * Add hooks to ensure PayPal IPN callbacks are added caches and considered for inventory changes
98 * when the sync happens. This also adds the shutdown hook to ensure sync happens if needed at
99 * a later stage.
100 *
101 * @since 2.1.1
102 *
103 * @param array $posted values returned from PayPal Standard IPN callback.
104 */
105 public function maybe_sync_stock_for_order_via_paypal( $posted ) {
106 if ( empty( $posted['custom'] ) ) {
107 return;
108 }
109
110 $raw_order = json_decode( $posted['custom'] );
111 if ( empty( $raw_order->order_id ) ) {
112 return;
113 }
114
115 $order = wc_get_order( $raw_order->order_id );
116
117 if ( ! $order || ! $order instanceof \WC_Order ) {
118 return;
119 }
120
121 $this->sync_stock_for_order( $order );
122 }
123
124 /**
125 * Checks if we should sync stock for this order.
126 * We only sync for other gateways that Square will not be aware of.
127 *
128 * This functions sets a process in motion that gathers products that will be processed on shutdown.
129 *
130 * @since 2.0.8
131 *
132 * @param int $order_id Order ID number.
133 * @param array $posted_data Submitted order data.
134 * @param WC_Order $order Order object.
135 */
136 public function maybe_sync_stock_for_order_via_other_gateway( $order_id, $posted_data, $order ) {
137
138 // Confirm we are not processing the order through the Square gateway.
139 if ( ! $order instanceof \WC_Order || Plugin::GATEWAY_ID === $order->get_payment_method() ) {
140 return;
141 }
142
143 $this->sync_stock_for_order( $order );
144 }
145
146 /**
147 * For a given order sync stock if inventory sync is enabled.
148 *
149 * @since 2.1.1
150 *
151 * @param \WC_Order $order the order for which the stock must be synced.
152 */
153 protected function sync_stock_for_order( $order ) {
154
155 if ( ! wc_square()->get_settings_handler()->is_inventory_sync_enabled() ) {
156 return;
157 }
158
159 $this->cache_previous_stock( $order );
160
161 add_action( 'woocommerce_product_set_stock', array( $this, 'maybe_stage_inventory_updates_for_product' ) );
162 add_action( 'woocommerce_variation_set_stock', array( $this, 'maybe_stage_inventory_updates_for_product' ) );
163
164 add_action( 'shutdown', array( $this, 'maybe_sync_staged_inventory_updates' ) );
165 }
166
167 /**
168 * Loop through order and cached previous stock values before they are reduced.
169 *
170 * @since 2.0.8
171 *
172 * @param WC_Order $order Order object.
173 */
174 private function cache_previous_stock( $order ) {
175
176 // Loop over all items.
177 foreach ( $order->get_items() as $item ) {
178 if ( ! $item->is_type( 'line_item' ) ) {
179 continue;
180 }
181
182 // Check to make sure it hasn't already been reduced.
183 $product = $item->get_product();
184 $item_stock_reduced = $item->get_meta( '_reduced_stock', true );
185
186 if ( $item_stock_reduced || ! $product || ! $product->managing_stock() ) {
187 continue;
188 }
189
190 $this->previous_stock[ $product->get_id() ] = $product->get_stock_quantity();
191 }
192 }
193
194 /**
195 * Stages a product inventory update for sync with Square when a product stock is updated.
196 *
197 * @internal The staged values will be stored in product_to_sync
198 *
199 * @since 2.0.8
200 *
201 * @param WC_Product $product the updated product with inventory updates.
202 */
203 public function maybe_stage_inventory_updates_for_product( $product ) {
204
205 // Do not add inventory changes if we are already doing a sync, or we are not syncing this product.
206 if ( defined( 'DOING_SQUARE_SYNC' ) || ! $product || ! Product::is_synced_with_square( $product ) ) {
207 return;
208 }
209
210 // Compare stock to get difference.
211 $product_id = $product->get_id();
212 $previous = isset( $this->previous_stock[ $product_id ] ) ? $this->previous_stock[ $product_id ] : false;
213 $current = $product->get_stock_quantity();
214 $adjustment = (int) $current - $previous;
215
216 if ( false === $previous || 0 === $adjustment ) {
217 return;
218 }
219
220 // Record what type of inventory action occurred.
221 $this->products_to_sync[ $product_id ] = $adjustment;
222 }
223
224
225 /**
226 * Initializes a synchronization event for any staged inventory updates in this request.
227 *
228 * @internal
229 *
230 * @since 2.0.8
231 */
232 public function maybe_sync_staged_inventory_updates() {
233
234 $inventory_adjustments = array();
235
236 foreach ( $this->products_to_sync as $product_id => $adjustment ) {
237
238 $product = wc_get_product( $product_id );
239 if ( ! $product instanceof \WC_Product ) {
240 continue;
241 }
242
243 $inventory_adjustment = Product::get_inventory_change_adjustment_type( $product, $adjustment );
244
245 if ( empty( $inventory_adjustment ) ) {
246 continue;
247 }
248
249 $inventory_adjustments[] = $inventory_adjustment;
250 }
251
252 if ( empty( $inventory_adjustments ) ) {
253 return;
254 }
255
256 wc_square()->log( 'New order from other gateway inventory syncing..' );
257 $idempotency_key = wc_square()->get_idempotency_key( md5( serialize( $inventory_adjustments ) ) . '_change_inventory' );
258 wc_square()->get_api()->batch_change_inventory( $idempotency_key, $inventory_adjustments );
259 }
260
261 /**
262 * Handle order refunds inventory/stock changes sync.
263 *
264 * @since 2.0.8
265 *
266 * @param in $order_id
267 * @param int $refund_id
268 */
269 public function maybe_sync_stock_for_refund_from_other_gateway( $order_id, $refund_id ) {
270
271 if ( ! wc_square()->get_settings_handler()->is_inventory_sync_enabled() ) {
272 return;
273 }
274
275 // Confirm we are not processing the order through the Square gateway.
276 $order = wc_get_order( $order_id );
277 if ( ! $order instanceof \WC_Order || Plugin::GATEWAY_ID === $order->get_payment_method() ) {
278 return;
279 }
280
281 // don't refund items if the "Restock refunded items" option is unchecked - maintains backwards compatibility if this function is called outside of the `woocommerce_order_refunded` do_action
282 if ( isset( $_POST['restock_refunded_items'] ) ) {
283 // Validate the user has permissions to process this request.
284 if ( ! check_ajax_referer( 'order-item', 'security', false ) || ! current_user_can( 'edit_shop_orders' ) ) {
285 return;
286 }
287
288 if ( 'false' === $_POST['restock_refunded_items'] ) {
289 return;
290 }
291 }
292
293 $refund = new \WC_Order_Refund( $refund_id );
294 $inventory_adjustments = array();
295 foreach ( $refund->get_items() as $item ) {
296
297 if ( 'line_item' !== $item->get_type() ) {
298 continue;
299 }
300
301 $product = $item->get_product();
302 if ( ! $product instanceof \WC_Product ) {
303 continue;
304 }
305
306 $adjustment = -1 * ( $item->get_quantity() ); // we want a positive value to increase the stock and a negative number to decrease it.
307 $inventory_adjustment = Product::get_inventory_change_adjustment_type( $product, $adjustment );
308
309 if ( empty( $inventory_adjustment ) ) {
310 continue;
311 }
312
313 $inventory_adjustments[] = $inventory_adjustment;
314 }
315
316 if ( empty( $inventory_adjustments ) ) {
317 return;
318 }
319
320 wc_square()->log( 'Order from other gateway Refund inventory updates syncing..' );
321 $idempotency_key = wc_square()->get_idempotency_key( md5( serialize( $inventory_adjustments ) ) . '_change_inventory' );
322 wc_square()->get_api()->batch_change_inventory( $idempotency_key, $inventory_adjustments );
323 }
324
325 /**
326 * Returns if the order was placed using a Square credit card.
327 *
328 * @since 3.7.0
329 *
330 * @param \WC_Order $order WooCommerce order.
331 *
332 * @return boolean
333 */
334 public static function is_tender_type_card( $order ) {
335 return '1' === wc_square()->get_gateway()->get_order_meta( $order, 'is_tender_type_card' );
336 }
337
338 /**
339 * @since 3.7.0
340 *
341 * @param \WC_Order $order WooCommerce order.
342 *
343 * @return boolean
344 */
345 public static function is_tender_type_gift_card( $order ) {
346 return '1' === wc_square()->get_gateway()->get_order_meta( $order, 'is_tender_type_gift_card' );
347 }
348
349 /**
350 * Sets the amount charged on the gift card for the given order.
351 *
352 * @since 3.7.0
353 *
354 * @param \WC_Order $order WooCommerce order.
355 * @param float $amount The total amount charged on the gift card for the order.
356 */
357 public static function set_gift_card_total_charged_amount( $order, $amount ) {
358 wc_square()->get_gateway()->update_order_meta( $order, 'gift_card_charged_amount', $amount );
359 }
360
361 /**
362 * Returns the amount charged on the gift card for the given order.
363 *
364 * @since 3.7.0
365 *
366 * @param \WC_Order $order WooCommerce order.
367 *
368 * @return float
369 */
370 public static function get_gift_card_total_charged_amount( $order ) {
371 return (float) wc_square()->get_gateway()->get_order_meta( $order, 'gift_card_charged_amount' );
372 }
373
374 /**
375 * Returns the last 4 digits of the gift card.
376 *
377 * @since 3.7.0
378 *
379 * @param \WC_Order $order WooCommerce order.
380 *
381 * @return string
382 */
383 public static function get_gift_card_last4( $order ) {
384 return wc_square()->get_gateway()->get_order_meta( $order, 'gift_card_last4' );
385 }
386
387 /**
388 * Sets the last 4 digits of the gift card.
389 *
390 * @since 3.7.0
391 *
392 * @param \WC_Order $order WooCommerce order.
393 *
394 * @return string
395 */
396 public static function set_gift_card_last4( $order, $number ) {
397 return wc_square()->get_gateway()->update_order_meta( $order, 'gift_card_last4', $number );
398 }
399
400 /**
401 * Returns the total amount that is refunded to the gift card.
402 *
403 * @since 3.7.0
404 *
405 * @param \WC_Order $order WooCommerce order.
406 *
407 * @return float
408 */
409 public static function get_gift_card_total_refunded_amount( $order ) {
410 $amount = (float) wc_square()->get_gateway()->get_order_meta( $order, 'gift_card_refunded_amount' );
411 $amount = empty( $amount ) ? 0 : $amount;
412
413 return $amount;
414 }
415
416 /**
417 * Sets the total amount that is refunded to the gift card.
418 *
419 * @since 3.7.0
420 *
421 * @param \WC_Order $order WooCommerce order.
422 * @param float $amount The total amount refunded to the gift card for the order.
423 */
424 public static function set_gift_card_total_refunded_amount( $order, $amount ) {
425 wc_square()->get_gateway()->update_order_meta( $order, 'gift_card_refunded_amount', $amount );
426 }
427
428 /**
429 * Sets the total order amount before applying the gift card.
430 *
431 * @since 3.7.0
432 *
433 * @param \WC_Order $order WooCommerce order.
434 * @param float $amount
435 */
436 public static function set_order_total_before_gift_card( $order, $amount ) {
437 wc_square()->get_gateway()->update_order_meta( $order, 'order_total_before_gift_card', $amount );
438 }
439
440 /**
441 * Gets the total order amount before applying the gift card.
442 *
443 * @since 3.7.0
444 *
445 * @return float
446 */
447 public static function get_order_total_before_gift_card( $order ) {
448 return (float) wc_square()->get_gateway()->get_order_meta( $order, 'order_total_before_gift_card' );
449 }
450
451 /**
452 * Displays the gift card details in the order item.
453 *
454 * @param int $order_id WooCommerce order ID.
455 */
456 public function add_admin_order_items( $order_id ) {
457 $order = wc_get_order( $order_id );
458
459 if ( ! self::is_tender_type_gift_card( $order ) ) {
460 return;
461 }
462
463 ?><tr class="square_gift_card item">
464 <td class="thumb">
465 <div style="width: 38px;">
466 <img src="<?php echo esc_url( WC_SQUARE_PLUGIN_URL . '/assets/images/gift-card.png' ); ?>" />
467 </div>
468 </td>
469 <td class="name">
470 <div class="view">
471 </div>
472 <div class="view">
473 <table cellspacing="0" class="display_meta">
474 <tbody>
475 <tr>
476 <th>
477 <?php esc_html_e( 'Square Gift Card:', 'woocommerce-square' ); ?>
478 </th>
479 <td>
480 <?php
481 printf(
482 /* Translators: %s - last 4 digits of the gift card. */
483 esc_html__( 'ending in %s', 'woocommerce-square' ),
484 esc_html( self::get_gift_card_last4( $order ) )
485 );
486 ?>
487 </td>
488 </tr>
489 </tbody>
490 </table>
491 </div>
492 </td>
493 <td class="item_cost" width="1%">&nbsp;</td>
494 <td class="quantity" width="1%">&nbsp;</td>
495 <td class="line_cost" width="1%">
496 <div class="view">-
497 <?php
498 echo wp_kses_post( wc_price( self::get_gift_card_total_charged_amount( $order ), array( 'currency' => $order->get_currency() ) ) );
499 $refunded_amount = self::get_gift_card_total_refunded_amount( $order );
500 ?>
501 </div>
502 </td>
503 <td class="wc-order-edit-line-item" width="1%">
504 </tr>
505 <?php
506 }
507
508 /**
509 * Includes info regarding gift card in the payment method title.
510 *
511 * @since 3.7.0
512 *
513 * @param string $value Payment method title.
514 * @param \WC_Order $order WooCommerce order.
515 *
516 * @return string
517 */
518 public function filter_payment_method_title( $value, $order ) {
519 if ( self::is_tender_type_gift_card( $order ) ) {
520 return esc_html__( 'Square Gift Card', 'woocommerce-square' );
521 }
522
523 return $value;
524 }
525
526 /**
527 * Includes info regarding gift card in the payment gateway title.
528 *
529 * @since 3.7.0
530 *
531 * @param string $value Gateway title.
532 * @param string $id Plugin id.
533 *
534 * @return string
535 */
536 public function filter_gateway_title( $value, $id ) {
537 if ( Plugin::GATEWAY_ID !== $id || ! is_admin() ) {
538 return $value;
539 }
540
541 $screen = get_current_screen();
542
543 if ( ! ( $screen && 'shop_order' === $screen->id ) ) {
544 return $value;
545 }
546
547 if ( ! isset( $_GET['post'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
548 return $value;
549 }
550
551 $post_id = wc_clean( absint( $_GET['post'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
552 $order = wc_get_order( $post_id );
553
554 if ( ! $order instanceof \WC_Order ) {
555 return $value;
556 }
557
558 if ( self::is_tender_type_gift_card( $order ) ) {
559 return esc_html__( 'Square Gift Card', 'woocommerce-square' );
560 }
561
562 return $value;
563 }
564 }
565