PluginProbe ʕ •ᴥ•ʔ
WooCommerce Square / 3.8.1
WooCommerce Square v3.8.1
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
558 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 ( check_ajax_referer( 'order-item', 'security', false ) && isset( $_POST['restock_refunded_items'] ) && 'false' === $_POST['restock_refunded_items'] ) {
283 return;
284 }
285
286 $refund = new \WC_Order_Refund( $refund_id );
287 $inventory_adjustments = array();
288 foreach ( $refund->get_items() as $item ) {
289
290 if ( 'line_item' !== $item->get_type() ) {
291 continue;
292 }
293
294 $product = $item->get_product();
295 if ( ! $product instanceof \WC_Product ) {
296 continue;
297 }
298
299 $adjustment = -1 * ( $item->get_quantity() ); // we want a positive value to increase the stock and a negative number to decrease it.
300 $inventory_adjustment = Product::get_inventory_change_adjustment_type( $product, $adjustment );
301
302 if ( empty( $inventory_adjustment ) ) {
303 continue;
304 }
305
306 $inventory_adjustments[] = $inventory_adjustment;
307 }
308
309 if ( empty( $inventory_adjustments ) ) {
310 return;
311 }
312
313 wc_square()->log( 'Order from other gateway Refund inventory updates syncing..' );
314 $idempotency_key = wc_square()->get_idempotency_key( md5( serialize( $inventory_adjustments ) ) . '_change_inventory' );
315 wc_square()->get_api()->batch_change_inventory( $idempotency_key, $inventory_adjustments );
316 }
317
318 /**
319 * Returns if the order was placed using a Square credit card.
320 *
321 * @since 3.7.0
322 *
323 * @param \WC_Order $order WooCommerce order.
324 *
325 * @return boolean
326 */
327 public static function is_tender_type_card( $order ) {
328 return '1' === wc_square()->get_gateway()->get_order_meta( $order, 'is_tender_type_card' );
329 }
330
331 /**
332 * @since 3.7.0
333 *
334 * @param \WC_Order $order WooCommerce order.
335 *
336 * @return boolean
337 */
338 public static function is_tender_type_gift_card( $order ) {
339 return '1' === wc_square()->get_gateway()->get_order_meta( $order, 'is_tender_type_gift_card' );
340 }
341
342 /**
343 * Sets the amount charged on the gift card for the given order.
344 *
345 * @since 3.7.0
346 *
347 * @param \WC_Order $order WooCommerce order.
348 * @param float $amount The total amount charged on the gift card for the order.
349 */
350 public static function set_gift_card_total_charged_amount( $order, $amount ) {
351 wc_square()->get_gateway()->update_order_meta( $order, 'gift_card_charged_amount', $amount );
352 }
353
354 /**
355 * Returns the amount charged on the gift card for the given order.
356 *
357 * @since 3.7.0
358 *
359 * @param \WC_Order $order WooCommerce order.
360 *
361 * @return float
362 */
363 public static function get_gift_card_total_charged_amount( $order ) {
364 return (float) wc_square()->get_gateway()->get_order_meta( $order, 'gift_card_charged_amount' );
365 }
366
367 /**
368 * Returns the last 4 digits of the gift card.
369 *
370 * @since 3.7.0
371 *
372 * @param \WC_Order $order WooCommerce order.
373 *
374 * @return string
375 */
376 public static function get_gift_card_last4( $order ) {
377 return wc_square()->get_gateway()->get_order_meta( $order, 'gift_card_last4' );
378 }
379
380 /**
381 * Sets the last 4 digits of the gift card.
382 *
383 * @since 3.7.0
384 *
385 * @param \WC_Order $order WooCommerce order.
386 *
387 * @return string
388 */
389 public static function set_gift_card_last4( $order, $number ) {
390 return wc_square()->get_gateway()->update_order_meta( $order, 'gift_card_last4', $number );
391 }
392
393 /**
394 * Returns the total amount that is refunded to the gift card.
395 *
396 * @since 3.7.0
397 *
398 * @param \WC_Order $order WooCommerce order.
399 *
400 * @return float
401 */
402 public static function get_gift_card_total_refunded_amount( $order ) {
403 $amount = (float) wc_square()->get_gateway()->get_order_meta( $order, 'gift_card_refunded_amount' );
404 $amount = empty( $amount ) ? 0 : $amount;
405
406 return $amount;
407 }
408
409 /**
410 * Sets the total amount that is refunded to the gift card.
411 *
412 * @since 3.7.0
413 *
414 * @param \WC_Order $order WooCommerce order.
415 * @param float $amount The total amount refunded to the gift card for the order.
416 */
417 public static function set_gift_card_total_refunded_amount( $order, $amount ) {
418 wc_square()->get_gateway()->update_order_meta( $order, 'gift_card_refunded_amount', $amount );
419 }
420
421 /**
422 * Sets the total order amount before applying the gift card.
423 *
424 * @since 3.7.0
425 *
426 * @param \WC_Order $order WooCommerce order.
427 * @param float $amount
428 */
429 public static function set_order_total_before_gift_card( $order, $amount ) {
430 wc_square()->get_gateway()->update_order_meta( $order, 'order_total_before_gift_card', $amount );
431 }
432
433 /**
434 * Gets the total order amount before applying the gift card.
435 *
436 * @since 3.7.0
437 *
438 * @return float
439 */
440 public static function get_order_total_before_gift_card( $order ) {
441 return (float) wc_square()->get_gateway()->get_order_meta( $order, 'order_total_before_gift_card' );
442 }
443
444 /**
445 * Displays the gift card details in the order item.
446 *
447 * @param int $order_id WooCommerce order ID.
448 */
449 public function add_admin_order_items( $order_id ) {
450 $order = wc_get_order( $order_id );
451
452 if ( ! self::is_tender_type_gift_card( $order ) ) {
453 return;
454 }
455
456 ?><tr class="square_gift_card item">
457 <td class="thumb">
458 <div style="width: 38px;">
459 <img src="<?php echo esc_url( WC_SQUARE_PLUGIN_URL . '/assets/images/gift-card.png' ); ?>" />
460 </div>
461 </td>
462 <td class="name">
463 <div class="view">
464 </div>
465 <div class="view">
466 <table cellspacing="0" class="display_meta">
467 <tbody>
468 <tr>
469 <th>
470 <?php esc_html_e( 'Square Gift Card:', 'woocommerce-square' ); ?>
471 </th>
472 <td>
473 <?php
474 printf(
475 /* Translators: %s - last 4 digits of the gift card. */
476 esc_html__( 'ending in %s', 'woocommerce-square' ),
477 esc_html( self::get_gift_card_last4( $order ) )
478 );
479 ?>
480 </td>
481 </tr>
482 </tbody>
483 </table>
484 </div>
485 </td>
486 <td class="item_cost" width="1%">&nbsp;</td>
487 <td class="quantity" width="1%">&nbsp;</td>
488 <td class="line_cost" width="1%">
489 <div class="view">-
490 <?php
491 echo wp_kses_post( wc_price( self::get_gift_card_total_charged_amount( $order ), array( 'currency' => $order->get_currency() ) ) );
492 $refunded_amount = self::get_gift_card_total_refunded_amount( $order );
493 ?>
494 </div>
495 </td>
496 <td class="wc-order-edit-line-item" width="1%">
497 </tr>
498 <?php
499 }
500
501 /**
502 * Includes info regarding gift card in the payment method title.
503 *
504 * @since 3.7.0
505 *
506 * @param string $value Payment method title.
507 * @param \WC_Order $order WooCommerce order.
508 *
509 * @return string
510 */
511 public function filter_payment_method_title( $value, $order ) {
512 if ( self::is_tender_type_gift_card( $order ) ) {
513 return esc_html__( 'Square Gift Card', 'woocommerce-square' );
514 }
515
516 return $value;
517 }
518
519 /**
520 * Includes info regarding gift card in the payment gateway title.
521 *
522 * @since 3.7.0
523 *
524 * @param string $value Gateway title.
525 * @param string $id Plugin id.
526 *
527 * @return string
528 */
529 public function filter_gateway_title( $value, $id ) {
530 if ( Plugin::GATEWAY_ID !== $id || ! is_admin() ) {
531 return $value;
532 }
533
534 $screen = get_current_screen();
535
536 if ( ! ( $screen && 'shop_order' === $screen->id ) ) {
537 return $value;
538 }
539
540 if ( ! isset( $_GET['post'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
541 return $value;
542 }
543
544 $post_id = wc_clean( absint( $_GET['post'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
545 $order = wc_get_order( $post_id );
546
547 if ( ! $order instanceof \WC_Order ) {
548 return $value;
549 }
550
551 if ( self::is_tender_type_gift_card( $order ) ) {
552 return esc_html__( 'Square Gift Card', 'woocommerce-square' );
553 }
554
555 return $value;
556 }
557 }
558