PluginProbe ʕ •ᴥ•ʔ
WooCommerce Square / 5.4.3
WooCommerce Square v5.4.3
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 / Framework / PaymentGateway / Admin / Payment_Gateway_Admin_Order.php
woocommerce-square / includes / Framework / PaymentGateway / Admin Last commit date
views 1 month ago Payment_Gateway_Admin_Order.php 9 months ago Payment_Gateway_Admin_Payment_Token_Editor.php 2 years ago Payment_Gateway_Admin_User_Handler.php 1 year ago
Payment_Gateway_Admin_Order.php
558 lines
1 <?php
2 /**
3 * WooCommerce Payment Gateway Framework
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@skyverge.com so we can send you a copy immediately.
12 *
13 * @since 3.0.0
14 * @author WooCommerce / SkyVerge
15 * @copyright Copyright (c) 2013-2019, SkyVerge, Inc.
16 * @license http://www.gnu.org/licenses/gpl-3.0.html GNU General Public License v3.0 or later
17 *
18 * Modified by WooCommerce on 01 December 2021.
19 */
20
21 namespace WooCommerce\Square\Framework\PaymentGateway\Admin;
22
23 use Square\Models\Money;
24 use WooCommerce\Square\Framework\PaymentGateway\Payment_Gateway_Plugin;
25 use WooCommerce\Square\Framework\PaymentGateway\Payment_Gateway;
26 use WooCommerce\Square\Framework\Square_Helper;
27 use WooCommerce\Square\Plugin;
28 use WooCommerce\Square\Framework\Compatibility\Order_Compatibility;
29 use WooCommerce\Square\Utilities\Money_Utility;
30
31 defined( 'ABSPATH' ) || exit;
32
33 /**
34 * Handle the admin order screens.
35 *
36 * @since 3.0.0
37 */
38 class Payment_Gateway_Admin_Order {
39
40
41 /** @var Payment_Gateway_Plugin the plugin instance **/
42 protected $plugin;
43
44
45 /**
46 * Constructs the class.
47 *
48 * @since 3.0.0
49 *
50 * @param Payment_Gateway_Plugin The plugin instance
51 */
52 public function __construct( Payment_Gateway_Plugin $plugin ) {
53
54 $this->plugin = $plugin;
55
56 add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
57
58 // capture feature
59 if ( $this->get_plugin()->supports_capture_charge() ) {
60
61 add_action( 'woocommerce_order_item_add_action_buttons', array( $this, 'add_capture_button' ) );
62
63 add_action( 'wp_ajax_wc_square_capture_charge', array( $this, 'ajax_process_capture' ) );
64
65 // bulk capture order action
66 add_action( 'admin_footer-edit.php', array( $this, 'maybe_add_capture_charge_bulk_order_action' ) );
67 add_action( 'load-edit.php', array( $this, 'process_capture_charge_bulk_order_action' ) );
68
69 // bulk capture order action - for HPOS
70 add_filter( 'bulk_actions-woocommerce_page_wc-orders', array( $this, 'maybe_add_capture_charge_bulk_actions' ) );
71 add_filter( 'handle_bulk_actions-woocommerce_page_wc-orders', array( $this, 'hpos_process_capture_charge_bulk_order_action' ), 10, 3 );
72 }
73 }
74
75
76 /**
77 * Enqueues the scripts and styles.
78 *
79 * @internal
80 *
81 * @since 3.0.0
82 *
83 * @param string $hook_suffix page hook suffix
84 */
85 public function enqueue_scripts( $hook_suffix ) {
86 global $post, $theorder;
87 if ( ! $post && ! $theorder ) {
88 return;
89 }
90
91 // get the order ID
92 $order_id = null;
93 if ( $theorder instanceof \WC_Order ) {
94 $order_id = $theorder->get_id();
95 } elseif ( is_a( $post, 'WP_Post' ) && 'shop_order' === get_post_type( $post ) ) {
96 $order_id = absint( $post->ID );
97 }
98
99 // Order screen assets
100 if ( ! empty( $order_id ) ) {
101
102 // Edit Order screen assets
103 if ( 'post.php' === $hook_suffix || 'post-new.php' === $hook_suffix || 'woocommerce_page_wc-orders' === $hook_suffix ) {
104
105 $order = wc_get_order( $order_id );
106
107 $this->enqueue_edit_order_assets( $order );
108 }
109 }
110 }
111
112
113 /**
114 * Enqueues the assets for the Edit Order screen.
115 *
116 * @since 3.0.0
117 *
118 * @param \WC_Order $order order object
119 */
120 protected function enqueue_edit_order_assets( \WC_Order $order ) {
121
122 wp_enqueue_script( 'payment-gateway-admin-order', $this->get_plugin()->get_plugin_url() . '/build/assets/admin/wc-square-payment-gateway-admin-order.js', array( 'jquery' ), Plugin::VERSION, true );
123
124 wp_localize_script(
125 'payment-gateway-admin-order',
126 'sv_wc_payment_gateway_admin_order',
127 array(
128 'ajax_url' => admin_url( 'admin-ajax.php' ),
129 'gateway_id' => Order_Compatibility::get_prop( $order, 'payment_method' ),
130 'order_id' => Order_Compatibility::get_prop( $order, 'id' ),
131 'capture_ays' => esc_html__( 'Are you sure you wish to process this capture? The action cannot be undone.', 'woocommerce-square' ),
132 'capture_action' => 'wc_square_capture_charge',
133 'capture_nonce' => wp_create_nonce( 'wc_square_capture_charge' ),
134 'capture_error' => esc_html__( 'Something went wrong, and the capture could no be completed. Please try again.', 'woocommerce-square' ),
135 'has_gift_card' => 'yes' === wc_square()->get_gateway( $order->get_payment_method() )->get_order_meta( $order, 'is_gift_card_purchased' ),
136 )
137 );
138
139 wp_enqueue_style( 'payment-gateway-admin-order', $this->get_plugin()->get_plugin_url() . '/build/assets/admin/wc-square-payment-gateway-admin-order.css', array(), Plugin::VERSION );
140 }
141
142
143 /** Capture Charge Feature ******************************************************/
144
145
146 /**
147 * Adds 'Capture charge' to the Orders screen bulk action select.
148 *
149 * @since 3.0.0
150 */
151 public function maybe_add_capture_charge_bulk_order_action() {
152 global $post_type, $post_status;
153
154 if ( ! current_user_can( 'edit_shop_orders' ) ) {
155 return;
156 }
157
158 if ( 'shop_order' === $post_type && 'trash' !== $post_status ) {
159
160 $can_capture_charge = false;
161
162 // ensure at least one gateway supports capturing charge
163 foreach ( $this->get_plugin()->get_gateways() as $gateway ) {
164
165 // ensure that it supports captures
166 if ( $gateway->supports_capture() ) {
167
168 $can_capture_charge = true;
169 break;
170 }
171 }
172
173 if ( $can_capture_charge ) {
174
175 ?>
176 <script type="text/javascript">
177 jQuery( document ).ready( function ( $ ) {
178 if ( 0 == $( 'select[name^=action] option[value=wc_capture_charge]' ).size() ) {
179 $( 'select[name^=action]' ).append(
180 $( '<option>' ).val( '<?php echo esc_js( 'wc_capture_charge' ); ?>' ).text( '<?php esc_html_e( 'Capture Charge', 'woocommerce-square' ); ?>' )
181 );
182 }
183 });
184 </script>
185 <?php
186 }
187 }
188 }
189
190 /**
191 * Adds 'Capture charge' to the Orders screen bulk action select (HPOS).
192 *
193 * @since 4.6.0
194 *
195 * @param array $bulk_actions bulk actions
196 * @return array
197 */
198 public function maybe_add_capture_charge_bulk_actions( $bulk_actions ) {
199 $can_capture_charge = false;
200 $status = sanitize_text_field( wp_unslash( $_REQUEST['status'] ?? '' ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
201
202 // phpcs:ignore WordPress.WP.Capabilities.Unknown
203 if ( ! current_user_can( 'edit_shop_orders' ) || 'trash' === $status ) {
204 return $bulk_actions;
205 }
206
207 // ensure at least one gateway supports capturing charge
208 foreach ( $this->get_plugin()->get_gateways() as $gateway ) {
209 // ensure that it supports captures
210 if ( $gateway->supports_capture() ) {
211 $can_capture_charge = true;
212 break;
213 }
214 }
215
216 if ( $can_capture_charge ) {
217 return array_merge( $bulk_actions, array( 'wc_capture_charge' => __( 'Capture Charge', 'woocommerce-square' ) ) );
218 }
219 return $bulk_actions;
220 }
221
222 /**
223 * Processes the 'Capture Charge' custom bulk action in HPOS.
224 *
225 * @since 4.6.0
226 */
227 public function hpos_process_capture_charge_bulk_order_action( $redirect_to, $action, $ids ) {
228 // bail if not processing a capture or if the user doesn't have the capability
229 // phpcs:ignore WordPress.WP.Capabilities.Unknown
230 if ( 'wc_capture_charge' !== $action || empty( $ids ) || ! current_user_can( 'edit_shop_orders' ) ) {
231 return;
232 }
233
234 // give ourselves an unlimited timeout if possible
235 @set_time_limit( 0 ); // phpcs:ignore
236
237 foreach ( $ids as $order_id ) {
238 $order = wc_get_order( $order_id );
239 if ( ! $order ) {
240 continue;
241 }
242 $gateway = $this->get_order_gateway( $order );
243 if ( $gateway ) {
244 $gateway->get_capture_handler()->maybe_perform_capture( $order );
245 }
246 }
247 }
248
249
250 /**
251 * Processes the 'Capture Charge' custom bulk action.
252 *
253 * @since 3.0.0
254 */
255 public function process_capture_charge_bulk_order_action() {
256 global $typenow;
257
258 if ( 'shop_order' === $typenow ) {
259
260 // get the action
261 $wp_list_table = _get_list_table( 'WP_Posts_List_Table' );
262 $action = $wp_list_table->current_action();
263
264 // bail if not processing a capture
265 if ( 'wc_capture_charge' !== $action ) {
266 return;
267 }
268
269 if ( ! current_user_can( 'edit_shop_orders' ) ) {
270 return;
271 }
272
273 // security check
274 check_admin_referer( 'bulk-posts' );
275
276 // make sure order IDs are submitted
277 if ( isset( $_REQUEST['post'] ) ) {
278 $order_ids = array_map( 'absint', $_REQUEST['post'] );
279 }
280
281 // return if there are no orders to export
282 if ( empty( $order_ids ) ) {
283 return;
284 }
285
286 // give ourselves an unlimited timeout if possible
287 @set_time_limit( 0 ); // phpcs:ignore
288
289 foreach ( $order_ids as $order_id ) {
290
291 $order = wc_get_order( $order_id );
292 if ( ! $order ) {
293 continue;
294 }
295 $gateway = $this->get_order_gateway( $order );
296 if ( $gateway ) {
297 $gateway->get_capture_handler()->maybe_perform_capture( $order );
298 }
299 }
300 }
301 }
302
303 /**
304 * Adds the capture charge button to the order UI.
305 *
306 * @internal
307 *
308 * @since 3.0.0
309 *
310 * @param \WC_Order $order order object
311 */
312 public function add_capture_button( $order ) {
313
314 if ( class_exists( '\Automattic\WooCommerce\Utilities\OrderUtil' ) ) {
315 $post_type = \Automattic\WooCommerce\Utilities\OrderUtil::get_order_type( $order->get_id() );
316 } else {
317 $post_type = get_post_type( Order_Compatibility::get_prop( $order, 'id' ) );
318 }
319
320 // only display the button for core orders
321 if ( ! $order instanceof \WC_Order || 'shop_order' !== $post_type ) {
322 return;
323 }
324
325 $gateway = $this->get_order_gateway( $order );
326
327 if ( ! $gateway ) {
328 return;
329 }
330
331 if ( ! $gateway->get_capture_handler()->is_order_ready_for_capture( $order ) ) {
332 return;
333 }
334
335 $tooltip = '';
336 $classes = array(
337 'button',
338 'wc-square-payment-gateway-capture',
339 'wc-' . $gateway->get_id_dasherized() . '-capture',
340 );
341
342 // indicate if the partial-capture UI can be shown
343 if ( $gateway->supports_partial_capture() && $gateway->is_partial_capture_enabled() ) {
344 $classes[] = 'partial-capture';
345 } elseif ( $gateway->get_capture_handler()->order_can_be_captured( $order ) ) {
346 $classes[] = 'button-primary';
347 }
348
349 // ensure that the authorization is still valid for capture
350 if ( ! $gateway->get_capture_handler()->order_can_be_captured( $order ) ) {
351
352 $classes[] = 'tips disabled';
353
354 // add some tooltip wording explaining why this cannot be captured
355 if ( $gateway->get_capture_handler()->is_order_fully_captured( $order ) ) {
356 $tooltip = esc_html__( 'This charge has been fully captured.', 'woocommerce-square' );
357 } elseif ( $gateway->get_order_meta( $order, 'trans_date' ) && $gateway->get_capture_handler()->has_order_authorization_expired( $order ) ) {
358 $tooltip = esc_html__( 'This charge can no longer be captured.', 'woocommerce-square' );
359 } else {
360 $tooltip = esc_html__( 'This charge cannot be captured.', 'woocommerce-square' );
361 }
362 }
363
364 ?>
365
366 <button type="button" class="<?php echo esc_attr( implode( ' ', $classes ) ); ?>" <?php echo ( $tooltip ) ? 'data-tip="' . esc_html( $tooltip ) . '"' : ''; ?>><?php esc_html_e( 'Capture Charge', 'woocommerce-square' ); ?></button>
367
368 <?php
369
370 // add the partial capture UI HTML
371 if ( $gateway->supports_partial_capture() && $gateway->is_partial_capture_enabled() ) {
372 $this->output_partial_capture_html( $order, $gateway );
373 }
374 }
375
376
377 /**
378 * Outputs the partial capture UI HTML.
379 *
380 * @since 3.0.0
381 *
382 * @param \WC_Order $order order object
383 * @param Payment_Gateway $gateway gateway instance
384 */
385 protected function output_partial_capture_html( \WC_Order $order, Payment_Gateway $gateway ) {
386
387 $authorization_total = $gateway->get_capture_handler()->get_order_authorization_amount( $order );
388 $total_captured = $gateway->get_order_meta( $order, 'capture_total' );
389 $remaining_total = Square_Helper::number_format( (float) $order->get_total() - (float) $total_captured );
390
391 include $this->get_plugin()->get_payment_gateway_framework_path() . '/Admin/views/html-order-partial-capture.php';
392 }
393
394
395 /**
396 * Processes a capture via AJAX.
397 *
398 * @internal
399 *
400 * @since 3.0.0
401 */
402 public function ajax_process_capture() {
403
404 check_ajax_referer( 'wc_square_capture_charge', 'nonce' );
405
406 $gateway_id = Square_Helper::get_request( 'gateway_id' );
407
408 if ( ! $this->get_plugin()->has_gateway( $gateway_id ) ) {
409 die();
410 }
411
412 /**
413 * @var \WooCommerce\Square\Gateway $gateway
414 */
415 $gateway = $this->get_plugin()->get_gateway( $gateway_id );
416
417 try {
418
419 $order_id = Square_Helper::get_request( 'order_id' );
420 $order = $gateway->get_order( $order_id );
421
422 if ( ! $order ) {
423 throw new \Exception( 'Invalid order ID' );
424 }
425
426 if ( ! current_user_can( 'edit_shop_order', $order_id ) ) {
427 throw new \Exception( 'Invalid permissions' );
428 }
429
430 if ( Order_Compatibility::get_prop( $order, 'payment_method' ) !== $gateway->get_id() ) {
431 throw new \Exception( 'Invalid payment method' );
432 }
433
434 if ( Square_Helper::get_request( 'amount' ) ) {
435 $amount = (float) Square_Helper::get_request( 'amount' );
436 } else {
437 $amount = (float) $order->get_total();
438 }
439
440 /**
441 * Before capture, the order may be updated by the merchant.
442 * So we fetch the payment and order, update them and then complete the payment.
443 *
444 * @see https://github.com/woocommerce/woocommerce-square/issues/693
445 */
446 $square_order = $gateway->get_api()->retrieve_order( $order->square_order_id );
447
448 $square_order_money = $square_order->getTotalMoney();
449 $square_order_amount = $square_order_money->getAmount();
450 $square_order_amount = Money_Utility::cents_to_float( $square_order_amount, $order->get_currency() );
451
452 if ( $square_order_amount !== $amount ) {
453 $gateway->get_api()->update_order( $order, $square_order );
454 $gateway->get_api()->update_payment( $order, Money_Utility::amount_to_cents( $amount, $order->get_currency() ) );
455 }
456
457 $result = $gateway->get_capture_handler()->perform_capture( $order, $amount );
458
459 if ( empty( $result['success'] ) ) {
460 throw new \Exception( $result['message'] );
461 }
462
463 wp_send_json_success(
464 array(
465 'message' => html_entity_decode( wp_strip_all_tags( $result['message'] ), ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401 ), // ensure any HTML tags are removed and the currency symbol entity is decoded
466 )
467 );
468
469 } catch ( \Exception $e ) {
470
471 wp_send_json_error(
472 array(
473 'message' => $e->getMessage(),
474 )
475 );
476 }
477 }
478
479
480 /**
481 * Gets the gateway object from an order.
482 *
483 * @since 3.0.0
484 *
485 * @param \WC_Order $order order object
486 * @return Payment_Gateway
487 */
488 protected function get_order_gateway( \WC_Order $order ) {
489
490 $capture_gateway = null;
491
492 $payment_method = Order_Compatibility::get_prop( $order, 'payment_method' );
493
494 if ( $this->get_plugin()->has_gateway( $payment_method ) ) {
495
496 $gateway = $this->get_plugin()->get_gateway( $payment_method );
497
498 // ensure that it supports captures
499 if ( $gateway->supports_capture() ) {
500 $capture_gateway = $gateway;
501 }
502 }
503
504 return $capture_gateway;
505 }
506
507
508 /**
509 * Gets the plugin instance.
510 *
511 * @since 3.0.0
512 *
513 * @return Payment_Gateway_Plugin the plugin instance
514 */
515 protected function get_plugin() {
516
517 return $this->plugin;
518 }
519
520 /**
521 * Captures an order on status change to a "paid" status.
522 *
523 * @internal
524 *
525 * @since 3.0.0
526 *
527 * @param int $order_id order ID
528 * @param string $old_status status being changed
529 * @param string $new_status new order status
530 */
531 public function maybe_capture_paid_order( $order_id, $old_status, $new_status ) {
532
533 wc_deprecated_function( __METHOD__, '3.0.0' );
534 }
535
536
537 /**
538 * Determines if an order is ready for capture.
539 *
540 * @since 3.0.0
541 *
542 * @param \WC_Order $order order object
543 * @return bool
544 */
545 protected function is_order_ready_for_capture( \WC_Order $order ) {
546
547 wc_deprecated_function( __METHOD__, '3.0.0' );
548
549 $gateway = $this->get_order_gateway( $order );
550
551 if ( ! $gateway ) {
552 return false;
553 }
554
555 return $gateway->get_capture_handler()->is_order_ready_for_capture( $order );
556 }
557 }
558