PluginProbe
WooCommerce Square / 5.0.0
WooCommerce Square v5.0.0
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 All 132 releases
woocommerce-square / includes / Plugin.php
Plugin.php
1,053 lines 28.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
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;
25
26 defined( 'ABSPATH' ) || exit;
27
28 use WooCommerce\Square\Framework\PaymentGateway\Payment_Gateway_Plugin;
29 use WooCommerce\Square\Framework\PaymentGateway\PaymentTokens\Square_Credit_Card_Payment_Token;
30 use WooCommerce\Square\Framework\Square_Helper;
31 use WooCommerce\Square\Gateway\Cash_App_Pay_Gateway;
32 use WooCommerce\Square\Gateway\Gift_Card;
33 use WooCommerce\Square\Handlers\Background_Job;
34 use WooCommerce\Square\Handlers\Async_Request;
35 use WooCommerce\Square\Handlers\Email;
36 use WooCommerce\Square\Handlers\Order;
37 use WooCommerce\Square\Handlers\Order_Sync;
38 use WooCommerce\Square\Handlers\Product;
39 use WooCommerce\Square\Handlers\Sync;
40 use WooCommerce\Square\Handlers\Products;
41
42 /**
43 * The main plugin class.
44 *
45 * @since 2.0.0
46 */
47 class Plugin extends Payment_Gateway_Plugin {
48
49
50 /** plugin version number */
51 const VERSION = WC_SQUARE_PLUGIN_VERSION;
52
53 /** plugin ID */
54 const PLUGIN_ID = 'square';
55
56 /** string gateway ID */
57 const GATEWAY_ID = 'square_credit_card';
58
59 /** string Gift Cards gateway ID */
60 const GIFT_CARD_PAY_GATEWAY_ID = 'gift_cards_pay';
61
62 /** string Cash App Pay gateway ID */
63 const CASH_APP_PAY_GATEWAY_ID = 'square_cash_app_pay';
64
65 /** @var Plugin plugin instance */
66 protected static $instance;
67
68 /** @var Settings settings handler instance */
69 private $settings_handler;
70
71 /** @var Handlers\Connection connection handler instance */
72 private $connection_handler;
73
74 /** @var Admin admin handler instance */
75 private $admin_handler;
76
77 /** @var Sync sync handler instance */
78 private $sync_handler;
79
80 /** @var Background_Job background handler instance */
81 private $background_job_handler;
82
83 /** @var AJAX handler instance */
84 private $ajax_handler;
85
86 /** @var Email emails handler */
87 private $email_handler;
88
89 /** @var Order orders handler */
90 private $order_handler;
91
92 /** @var Products products handler */
93 private $products_handler;
94
95 /** @var Async_Request Asynchronous request handler */
96 private $async_request_handler;
97
98 /** @var Order_Sync order sync handler */
99 private $order_sync_handler;
100
101 /**
102 * Constructs the plugin.
103 *
104 * @since 2.0.0
105 */
106 public function __construct() {
107
108 parent::__construct(
109 self::PLUGIN_ID,
110 self::VERSION,
111 array(
112 'text_domain' => 'woocommerce-square',
113 'gateways' => array(
114 self::GATEWAY_ID => Gateway::class,
115 self::CASH_APP_PAY_GATEWAY_ID => Cash_App_Pay_Gateway::class,
116 self::GIFT_CARD_PAY_GATEWAY_ID => Gift_Card::class,
117 ),
118 'require_ssl' => true,
119 'supports' => array(
120 self::FEATURE_CAPTURE_CHARGE,
121 self::FEATURE_CUSTOMER_ID,
122 self::FEATURE_MY_PAYMENT_METHODS,
123 ),
124 'dependencies' => array(
125 'php_extensions' => array( 'curl', 'json', 'mbstring' ),
126 ),
127 )
128 );
129
130 $this->includes();
131
132 /**
133 * Fires upon plugin loaded (legacy hook).
134 *
135 * @since 1.0.0
136 */
137 do_action( 'wc_square_loaded' );
138
139 add_action( 'woocommerce_register_taxonomy', array( $this, 'init_taxonomies' ) );
140 add_action( 'admin_notices', array( $this, 'add_admin_notices' ) );
141 add_filter( 'woocommerce_locate_template', array( $this, 'locate_template' ), 20, 3 );
142 add_filter( 'woocommerce_locate_core_template', array( $this, 'locate_template' ), 20, 3 );
143
144 add_action( 'action_scheduler_init', array( $this, 'schedule_token_migration_job' ) );
145 add_action( 'wc_square_init_payment_token_migration_v2', array( $this, 'register_payment_tokens_migration_scheduler' ) );
146 add_action( 'wc_square_init_payment_token_migration', '__return_false' );
147
148 // Unschedule order sync event if order sync is disabled.
149 add_action( 'admin_init', array( $this, 'unschedule_order_sync' ) );
150 }
151
152 /**
153 * Unschedule order sync event if order sync is disabled.
154 *
155 * @since 5.0.0
156 */
157 public function unschedule_order_sync() {
158 if ( ! $this->get_settings_handler()->is_order_fulfillment_sync_enabled() && as_has_scheduled_action( WC_SQUARE_SYNC_ORDERS_EVENT_HOOK, array(), wc_square()->get_id() ) ) {
159 // Delete the order sync event.
160 as_unschedule_all_actions( WC_SQUARE_SYNC_ORDERS_EVENT_HOOK, array(), wc_square()->get_id() );
161 }
162 }
163
164 /**
165 * Includes required classes.
166 *
167 * @since 2.0.0
168 */
169 private function includes() {
170
171 $this->connection_handler = new Handlers\Connection( $this );
172
173 $this->sync_handler = new Sync( $this );
174
175 // background export must be loaded all the time, because otherwise background jobs simply won't work
176 require_once $this->get_framework_path() . '/Utilities/WP_Background_Job_Handler.php';
177
178 $this->background_job_handler = new Background_Job();
179
180 $this->ajax_handler = new AJAX();
181
182 $this->email_handler = new Email();
183
184 $this->order_handler = new Order();
185
186 if ( class_exists( '\WooCommerce\Square\Handlers\Async_Request' ) ) {
187 $this->async_request_handler = new Async_Request();
188 }
189 }
190
191
192 /**
193 * Adds API request logging.
194 *
195 * @internal
196 *
197 * @since 2.0.0
198 */
199 public function add_api_request_logging() {
200
201 if ( ! has_action( 'wc_square_api_request_performed' ) ) {
202 add_action( 'wc_square_api_request_performed', array( $this, 'log_api_request' ), 10, 2 );
203 }
204 }
205
206
207 /**
208 * Logs an API request & response.
209 *
210 * @since 2.0.0
211 *
212 * @param array $request request data
213 * @param array $response response data
214 * @param string|null $log_id log ID
215 */
216 public function log_api_request( $request, $response, $log_id = null ) {
217
218 if ( $this->get_settings_handler() && $this->get_settings_handler()->is_debug_enabled() ) {
219 parent::log_api_request( $request, $response, $log_id );
220 }
221 }
222
223
224 /**
225 * If debug logging is enabled, saves errors or messages to Square Log
226 *
227 * @since 2.2.4
228 * @param string $message error or message to save to log
229 * @param string $log_id optional log id to segment the files by, defaults to plugin id
230 */
231 public function log( $message, $log_id = null ) {
232
233 if ( $this->get_settings_handler() && $this->get_settings_handler()->is_debug_enabled() ) {
234 parent::log( $message, $log_id );
235 }
236 }
237
238
239 /**
240 * Initializes the lifecycle handler.
241 *
242 * @since 2.0.0
243 */
244 public function init_lifecycle_handler() {
245
246 $this->lifecycle_handler = new Lifecycle( $this );
247 }
248
249
250 /**
251 * Registers custom taxonomies.
252 *
253 * @internal
254 *
255 * @since 2.0.0
256 */
257 public function init_taxonomies() {
258
259 Product::init_taxonomies();
260 }
261
262
263 /**
264 * Initializes the general plugin functionality.
265 *
266 * @since 2.0.0
267 */
268 public function init_plugin() {
269
270 $this->settings_handler = new Settings( $this );
271 $this->products_handler = new Products( $this );
272
273 if ( ! $this->admin_handler && is_admin() ) {
274 $this->admin_handler = new Admin( $this );
275 }
276
277 // Initialize order sync handler if order sync is enabled.
278 if ( $this->get_settings_handler()->is_order_fulfillment_sync_enabled() ) {
279 $this->order_sync_handler = new Order_Sync();
280 $this->order_sync_handler->init();
281 }
282
283 // WooPayments compatibility.
284 $wcpay_compatibility = new WC_Payments_Compatibility();
285 $wcpay_compatibility->init();
286
287 /**
288 * @see wc_square_initialized
289 * @since 2.0.0
290 */
291 do_action( 'wc_square_initialized' );
292 }
293
294
295 /**
296 * Locates the WooCommerce template files from our templates directory.
297 *
298 * @internal
299 *
300 * @since 2.0.0
301 *
302 * @param string $template already found template
303 * @param string $template_name searchable template name
304 * @param string $template_path template path
305 * @return string search result for the template
306 */
307 public function locate_template( $template, $template_name, $template_path ) {
308
309 // only keep looking if no custom theme template was found
310 // or if a default WooCommerce template was found
311 if ( ! $template || Square_Helper::str_starts_with( $template, WC()->plugin_path() ) ) {
312
313 // set the path to our templates directory
314 $plugin_path = $this->get_plugin_path() . '/templates/';
315
316 // if a template is found, make it so
317 if ( is_readable( $plugin_path . $template_name ) ) {
318 $template = $plugin_path . $template_name;
319 }
320 }
321
322 return $template;
323 }
324
325
326 /** Admin methods *************************************************************************************************/
327
328
329 /**
330 * Adds admin notices.
331 *
332 * @since 2.0.0
333 */
334 public function add_admin_notices() {
335
336 // show any one-off messages
337 $this->get_message_handler()->show_messages();
338
339 // display a notice if the auto-refresh failed
340 if ( get_option( 'wc_square_refresh_failed', false ) ) {
341
342 $message = sprintf(
343 /* translators: Placeholders: %1$s - <a> tag, %2$s - </a> tag */
344 __( 'Heads up! There may be a problem with your connection to Square. In order to continue accepting payments, please %1$sdisconnect and re-connect your site%2$s.', 'woocommerce-square' ),
345 '<a href="' . esc_url( $this->get_settings_url() ) . '">',
346 '</a>'
347 );
348
349 $this->get_admin_notice_handler()->add_admin_notice(
350 $message,
351 'refresh-failed',
352 array(
353 'dismissible' => false,
354 'notice_class' => 'notice-warning',
355 )
356 );
357 }
358
359 if ( $this->get_settings_handler()->is_connected() ) {
360
361 $message = '<strong>' . __( 'You are connected to Square!', 'woocommerce-square' ) . '</strong>';
362
363 // prompt to set a location if not set
364 if ( ! $this->get_settings_handler()->get_location_id() ) {
365
366 if ( $this->is_plugin_settings() ) {
367
368 $instruction = __( 'To get started, set your business location.', 'woocommerce-square' );
369
370 } else {
371
372 $instruction = sprintf(
373 /* translators: Placeholders: %1$s - <a> tag, %2$s - </a> tag */
374 __( 'Visit the %1$splugin settings%2$s to set your business location.', 'woocommerce-square' ),
375 '<a href="' . esc_url( $this->get_settings_url() ) . '">',
376 '</a>'
377 );
378 }
379
380 $this->get_admin_notice_handler()->add_admin_notice( $message . ' ' . $instruction, 'set-location' );
381
382 } elseif ( ! $this->get_sync_handler()->get_last_synced_at() && $this->get_settings_handler()->is_product_sync_enabled() ) {
383
384 $message = __( 'You are ready to sync products!', 'woocommerce-square' );
385
386 if ( ! empty( Product::get_products_synced_with_square() ) ) {
387
388 $instruction = sprintf(
389 /* translators: Placeholders: %1$s - <strong> tag, %2$s - product count, %3$s - </strong> tag, %4$s - <a> tag, %5$s - </a> tag */
390 __( '%1$s%2$d products%3$s are marked "sync with Square". %4$sStart a new sync now &raquo;%5$s', 'woocommerce-square' ),
391 '<strong>',
392 count( Product::get_products_synced_with_square() ),
393 '</strong>',
394 '<a href="' . esc_url( add_query_arg( 'section', 'update', $this->get_settings_url() ) ) . '">',
395 '</a>'
396 );
397
398 } else {
399
400 $instruction = sprintf(
401 /* translators: Placeholders: %1$s - <strong> tag, %2$s - </strong> tag, %3$s - <a> tag, %4$s - </a> tag */
402 __( '%1$sNo products%2$s are marked "sync with Square". %3$sUpdate your products to sync data &raquo;%4$s', 'woocommerce-square' ),
403 '<strong>',
404 '</strong>',
405 '<a href="' . esc_url( admin_url( 'edit.php?post_type=product' ) ) . '">',
406 '</a>'
407 );
408 }
409
410 $this->get_admin_notice_handler()->add_admin_notice( $message . ' ' . $instruction, 'set-location' );
411 }
412
413 // a notice for when WC stock handling is globally disabled
414 if ( 'yes' !== get_option( 'woocommerce_manage_stock' ) && $this->get_settings_handler()->is_inventory_sync_enabled() ) {
415
416 $message = sprintf(
417 /* translators: Placeholders: %1$s - <a> tag, %2$s - </a> tag */
418 __( 'Heads up! Square is configured to sync product inventory, but WooCommerce stock management is disabled. Please %1$senable stock management%2$s to ensure product inventory counts are kept in sync.', 'woocommerce-square' ),
419 '<a href="' . esc_url( admin_url( 'admin.php?page=wc-settings&tab=products&section=inventory' ) ) . '">',
420 '</a>'
421 );
422
423 $this->get_admin_notice_handler()->add_admin_notice(
424 $message,
425 'enable-wc-sync',
426 array(
427 'notice_class' => 'notice-warning',
428 )
429 );
430 }
431 }
432
433 // add a notice for out-of-bounds base locations
434 $this->add_base_location_admin_notice();
435
436 // add a notice when no refresh token is available
437 $this->add_missing_refresh_token_notice();
438
439 // add a tax-inclusive warning to product pages
440 $this->add_tax_inclusive_pricing_notice();
441
442 if ( get_option( 'wc_square_updated_to_2_0_0' ) ) {
443
444 $this->get_admin_notice_handler()->add_admin_notice(
445 sprintf(
446 /* translators: Placeholders: %1$s - plugin name, %2$ - plugin version number, %3$s - opening <a> HTML link tag, %4$s - closing </a> HTML link tag, %5$s - opening <a> HTML link tag, %6$s - closing </a> HTML link tag*/
447 esc_html__( '%1$s has been updated to version %2$s. In order to continue syncing product inventory, please make sure to disconnect and reconnect with Square from the %3$splugin settings%4$s and re-sync your products. Read more in the %5$supdated documentation%6$s.', 'woocommerce-square' ),
448 '<strong>' . esc_html( $this->get_plugin_name() ) . '</strong>',
449 $this->get_version(),
450 '<a href="' . esc_url( $this->get_settings_url() ) . '">',
451 '</a>',
452 '<a href="' . esc_url( $this->get_documentation_url() ) . '">',
453 '</a>'
454 ),
455 'updated-to-v2',
456 array( 'notice_class' => 'notice-warning' )
457 );
458 }
459 }
460
461
462 /**
463 * Adds a notice for out-of-bounds base locations.
464 *
465 * @since 2.0.0
466 */
467 protected function add_base_location_admin_notice() {
468
469 $accepted_countries = array(
470 'US',
471 'CA',
472 'GB',
473 'AU',
474 'JP',
475 'IE',
476 'FR',
477 'ES',
478 );
479
480 $base_location = wc_get_base_location();
481
482 if ( isset( $base_location['country'] ) && ! in_array( $base_location['country'], $accepted_countries, true ) ) {
483
484 $this->get_admin_notice_handler()->add_admin_notice(
485 sprintf(
486 /* translators: Placeholders: %1$s - <strong> tag, %2$s - </strong> tag, %3$s - 2-character country code, %4$s - comma separated list of 2-character country codes */
487 __( '%1$sWooCommerce Square:%2$s Your base country is %3$s, but Square can’t accept transactions from merchants outside of %4$s.', 'woocommerce-square' ),
488 '<strong>',
489 '</strong>',
490 esc_html( $base_location['country'] ),
491 esc_html( Square_Helper::list_array_items( $accepted_countries ) )
492 ),
493 'wc-square-base-location',
494 array(
495 'notice_class' => 'notice-error',
496 )
497 );
498 }
499 }
500
501 /**
502 * Adds a notice if no refresh token has been cached.
503 *
504 * @since 2.0.5
505 */
506 protected function add_missing_refresh_token_notice() {
507 if ( $this->get_settings_handler()->is_sandbox() ) {
508 return;
509 }
510
511 $refresh_token = '';
512 $settings_handler = $this->get_settings_handler();
513
514 if ( method_exists( $settings_handler, 'get_access_token' ) ) {
515 $access_token = $settings_handler->get_access_token();
516 if ( empty( $access_token ) ) {
517 // We are already in a disconnected state, don't show the warning.
518 return;
519 }
520 }
521
522 if ( method_exists( $settings_handler, 'get_refresh_token' ) ) {
523 $refresh_token = $settings_handler->get_refresh_token();
524 }
525
526 if ( empty( $refresh_token ) ) {
527 $this->get_admin_notice_handler()->add_admin_notice(
528 sprintf(
529 /* translators: Placeholders: %1$s - <strong> tag, %2$s - </strong> tag, %3$s - <a> tag, %4$s - </a> tag */
530 __( '%1$sWooCommerce Square:%2$s Automatic refreshing of the connection to Square is inactive. Please disconnect and reconnect to resolve.', 'woocommerce-square' ),
531 '<strong>',
532 '</strong>'
533 ),
534 'wc-square-missing-refresh-token',
535 array(
536 'dismissible' => false,
537 'notice_class' => 'notice-error',
538 )
539 );
540 }
541 }
542
543
544 /**
545 * Adds a tax-inclusive admin warning to product pages.
546 *
547 * @since 2.0.0
548 */
549 protected function add_tax_inclusive_pricing_notice() {
550 global $typenow;
551
552 // only show on product edit pages when configured that prices include tax
553 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce not required, not writing any changes.
554 if ( 'product' === $typenow && isset( $_GET['action'], $_GET['post'] ) && 'edit' === $_GET['action'] && wc_prices_include_tax() && $this->get_settings_handler()->is_product_sync_enabled() ) {
555
556 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce not required, not writing any changes.
557 $product = wc_get_product( (int) $_GET['post'] );
558
559 // only show for products configured as taxable and sync with Square
560 if ( $product instanceof \WC_Product && $product->is_taxable() && Product::is_synced_with_square( $product ) ) {
561
562 $this->get_admin_notice_handler()->add_admin_notice(
563 sprintf(
564 /* translators: Placeholders: %1$s = <strong> tag, %2$s = </strong> tag */
565 __( '%1$sWooCommerce Square:%2$s Product prices are entered inclusive of tax, but Square does not support syncing tax-inclusive prices. Please make sure your Square tax rates match your WooCommerce tax rates.', 'woocommerce-square' ),
566 '<strong>',
567 '</strong>'
568 ),
569 'wc-square-tax-inclusive',
570 array(
571 'notice_class' => 'notice-warning',
572 )
573 );
574 }
575 }
576 }
577
578
579 /**
580 * Adds admin notices for currency issues.
581 *
582 * @since 2.0.0
583 */
584 protected function add_currency_admin_notices() {
585
586 parent::add_currency_admin_notices();
587
588 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce not required, only showing a notice.
589 if ( isset( $_GET['page'] ) && 'wc-settings' === $_GET['page'] && $this->get_settings_handler()->is_connected() ) {
590
591 foreach ( $this->get_settings_handler()->get_locations() as $location ) {
592
593 if ( $this->get_settings_handler()->get_location_id() === $location->getId() && get_woocommerce_currency() !== $location->getCurrency() ) {
594
595 $this->get_admin_notice_handler()->add_admin_notice(
596 sprintf(
597 /* translators: Placeholders: %1$s = store currency, %2$s = configured Square business location currency, %3$s = <a> tag, %4$s = </a> tag, %5$s = <a> tag, %6$s = </a> tag */
598 __( 'Heads up! Your store currency is %1$s but your configured Square business location currency is %2$s, so payments cannot be processed. Please %3$schoose a different business location%4$s or change your %5$sshop currency%6$s.', 'woocommerce-square' ),
599 '<strong>' . esc_html( get_woocommerce_currency() ) . '</strong>',
600 '<strong>' . esc_html( $location->getCurrency() ) . '</strong>',
601 '<a href="' . esc_url( $this->get_settings_url() ) . '">',
602 '</a>',
603 '<a href="' . esc_url( admin_url( 'admin.php?page=wc-settings' ) ) . '">',
604 '</a>'
605 ),
606 'wc-square-currency-mismatch',
607 array(
608 'notice_class' => 'notice-error',
609 )
610 );
611 }
612 }
613 }
614 }
615
616
617 /** Helper methods ************************************************************************************************/
618
619
620 /**
621 * Returns an idempotency key to be used in Square API requests.
622 *
623 * @since 2.0.0
624 *
625 * @param string $key_input
626 * @param bool $append_key_input
627 * @return string
628 */
629 public function get_idempotency_key( $key_input = '', $append_key_input = true ) {
630
631 if ( '' === $key_input ) {
632 $key_input = uniqid( '', false );
633 }
634
635 /**
636 * Filters an idempotency key.
637 *
638 * @since 2.0.0
639 *
640 * @param string $key_input
641 */
642 return apply_filters( 'wc_square_idempotency_key', sha1( get_option( 'siteurl' ) . $key_input ) . ( $append_key_input ? ':' . $key_input : '' ) );
643 }
644
645
646 /** Conditional methods *******************************************************************************************/
647
648
649 /**
650 * Determines if viewing the plugin settings.
651 *
652 * @since 2.0.0
653 *
654 * @return bool
655 */
656 public function is_plugin_settings() {
657 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce note required, read-only check.
658 return parent::is_plugin_settings() || ( isset( $_GET['page'], $_GET['tab'] ) && 'wc-settings' === $_GET['page'] && self::PLUGIN_ID === $_GET['tab'] );
659 }
660
661 /**
662 * Determines if viewing the gateway settings.
663 *
664 * @since 2.3.0
665 *
666 * @return bool
667 */
668 public function is_gateway_settings() {
669 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce note required, read-only check.
670 return isset( $_GET['page'], $_GET['tab'], $_GET['section'] ) && 'wc-settings' === $_GET['page'] && 'checkout' === $_GET['tab'] && self::GATEWAY_ID === $_GET['section'];
671 }
672
673
674 /** Getter methods ************************************************************************************************/
675
676
677 /**
678 * Gets the main Square API handler.
679 *
680 * @since 2.0.0
681 *
682 * @param string|null $access_token API access token
683 * @return API
684 */
685 public function get_api( $access_token = null, $is_sandbox = null ) {
686
687 if ( ! $access_token ) {
688 $access_token = $this->get_settings_handler()->get_access_token();
689 }
690
691 if ( is_null( $is_sandbox ) ) {
692 $is_sandbox = $this->get_settings_handler()->is_sandbox();
693 }
694
695 return new API( $access_token, $is_sandbox );
696 }
697
698
699 /**
700 * Gets the connection handler.
701 *
702 * @since 2.0.0
703 *
704 * @return Handlers\Connection
705 */
706 public function get_connection_handler() {
707
708 return $this->connection_handler;
709 }
710
711
712 /**
713 * Gets the sync handler instance.
714 *
715 * @since 2.0.0
716 *
717 * @return Sync
718 */
719 public function get_sync_handler() {
720
721 return $this->sync_handler;
722 }
723
724
725 /**
726 * Gets the background sync handler instance.
727 *
728 * @since 2.0.0
729 *
730 * @return Background_Job
731 */
732 public function get_background_job_handler() {
733
734 return $this->background_job_handler;
735 }
736
737
738 /**
739 * Gets the settings handler instance.
740 *
741 * @since 2.0.0
742 *
743 * @return Settings
744 */
745 public function get_settings_handler() {
746
747 return $this->settings_handler;
748 }
749
750
751
752 /**
753 * Gets the admin handler instance.
754 *
755 * @since 2.0.0
756 *
757 * @return Admin|null
758 */
759 public function get_admin_handler() {
760
761 // throw a notice if calling before admin_init
762 Square_Helper::maybe_doing_it_early( 'admin_init', __METHOD__, '2.0.0' );
763
764 return $this->admin_handler;
765 }
766
767
768 /**
769 * Gets the email handler instance.
770 *
771 * @since 2.0.0
772 *
773 * @return Email
774 */
775 public function get_email_handler() {
776
777 return $this->email_handler;
778 }
779
780
781 /**
782 * Gets the order handler instance.
783 *
784 * @since 2.0.0
785 *
786 * @return Order
787 */
788 public function get_order_handler() {
789
790 return $this->order_handler;
791 }
792
793 /**
794 * Get the products handler instance/
795 *
796 * @since 2.0.8
797 *
798 * @return Products
799 */
800 public function get_products_handler() {
801 return $this->products_handler;
802 }
803
804 /**
805 * Gets the Asynchronous request handler instance.
806 *
807 * @since 4.1.0
808 *
809 * @return Async_Request
810 */
811 public function get_async_request_handler() {
812 return $this->async_request_handler;
813 }
814
815 /**
816 * Gets the order sync handler instance.
817 *
818 * @since 5.0.0
819 *
820 * @return Order_Sync
821 */
822 public function get_order_sync_handler() {
823 return $this->order_sync_handler;
824 }
825
826 /**
827 * Gets the plugin name.
828 *
829 * @since 2.0.0
830 *
831 * @return string
832 */
833 public function get_plugin_name() {
834
835 return __( 'WooCommerce Square', 'woocommerce-square' );
836 }
837
838
839 /**
840 * Gets the settings URL.
841 *
842 * @since 2.0.0
843 *
844 * @param null|string $gateway_id gateway ID
845 * @return string
846 */
847 public function get_settings_url( $gateway_id = null ) {
848
849 $params = array(
850 'page' => 'wc-settings',
851 'tab' => self::PLUGIN_ID,
852 );
853
854 // All usage of this return value has been escaped late.
855 // nosemgrep audit.php.wp.security.xss.query-arg
856 return add_query_arg( $params, admin_url( 'admin.php' ) );
857 }
858
859 /**
860 * Gets the Setup Wizard URL.
861 *
862 * @since 4.7.0
863 *
864 * @param string $step Step to go to.
865 *
866 * @return string
867 */
868 public function get_square_onboarding_url( $step = '' ) {
869 $params = array(
870 'page' => 'woocommerce-square-onboarding',
871 );
872
873 // Add 'step' if $step is not empty.
874 if ( ! empty( $step ) ) {
875 $params['step'] = $step;
876 }
877
878 // All usage of this return value has been escaped late.
879 // nosemgrep audit.php.wp.security.xss.query-arg
880 return add_query_arg( $params, admin_url( 'admin.php' ) );
881 }
882
883
884 /**
885 * Gets the sale page URL.
886 *
887 * @since 2.0.0
888 *
889 * @return string
890 */
891 public function get_sales_page_url() {
892
893 return 'https://woocommerce.com/products/square/';
894 }
895
896
897 /**
898 * Gets the documentation URL.
899 *
900 * @since 2.0.0
901 *
902 * @return string
903 */
904 public function get_documentation_url() {
905
906 return 'https://docs.woocommerce.com/document/woocommerce-square/';
907 }
908
909
910 /**
911 * Gets the plugin reviews page URL.
912 *
913 * Used for the 'Reviews' plugin action and review prompts.
914 *
915 * @since 2.1.7
916 *
917 * @return string
918 */
919 public function get_reviews_url() {
920
921 return $this->get_sales_page_url() ? $this->get_sales_page_url() . '#comments' : '';
922 }
923
924
925 /**
926 * Gets the support URL.
927 *
928 * @since 2.0.0
929 *
930 * @return string
931 */
932 public function get_support_url() {
933
934 return 'https://woocommerce.com/my-account/create-a-ticket/?select=1770503';
935 }
936
937
938 /**
939 * Gets __DIR__.
940 *
941 * @since 2.0.0
942 *
943 * @return string
944 */
945 protected function get_file() {
946
947 return __DIR__;
948 }
949
950
951 /**
952 * Gets the singleton instance of the plugin.
953 *
954 * @since 2.0.0
955 *
956 * @return Plugin
957 */
958 public static function instance() {
959
960 if ( null === self::$instance ) {
961 self::$instance = new self();
962 }
963
964 return self::$instance;
965 }
966
967 /**
968 * Schedules the migration of payment tokens.
969 *
970 * @since 3.8.0
971 */
972 public function schedule_token_migration_job() {
973 if ( false !== get_option( 'wc_square_payment_token_migration_complete' ) ) {
974 return;
975 }
976
977 // Remove all OLD scheduled actions to cleanup DB.
978 // TODO: Remove this in next release.
979 global $wpdb;
980 $wpdb->query( "DELETE FROM {$wpdb->prefix}actionscheduler_actions WHERE hook = 'wc_square_init_payment_token_migration'" );
981
982 if ( false === as_has_scheduled_action( 'wc_square_init_payment_token_migration_v2' ) ) {
983 as_enqueue_async_action( 'wc_square_init_payment_token_migration_v2', array( 'page' => 1 ) );
984 }
985 }
986
987 /**
988 * Migrates payment token from user_meta to WC_Payment_Token_CC.
989 *
990 * @param integer $page Pagination number.
991 * @since 3.8.0
992 */
993 public function register_payment_tokens_migration_scheduler( $page ) {
994 $payment_tokens_handler = wc_square()->get_gateway()->get_payment_tokens_handler();
995 $meta_key = $payment_tokens_handler->get_user_meta_name();
996
997 // Get 5 users in a batch.
998 $users = get_users(
999 array(
1000 'fields' => array( 'ID' ),
1001 'number' => 5,
1002 'paged' => $page,
1003 'meta_query' => array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
1004 array(
1005 'key' => $meta_key,
1006 'compare' => 'EXISTS',
1007 ),
1008 ),
1009 )
1010 );
1011
1012 // If users array is empty, then set status in options to indicate migration is complete.
1013 if ( empty( $users ) ) {
1014 $payment_tokens_handler->clear_all_transients();
1015 update_option( 'wc_square_payment_token_migration_complete', true );
1016 return;
1017 }
1018
1019 // Re-run scheduler for the next page of users.
1020 as_enqueue_async_action( 'wc_square_init_payment_token_migration_v2', array( 'page' => $page + 1 ) );
1021
1022 foreach ( $users as $user ) {
1023 $user_payment_tokens = get_user_meta( $user->id, $meta_key, true );
1024
1025 if ( ! is_array( $user_payment_tokens ) || empty( $user_payment_tokens ) ) {
1026 continue;
1027 }
1028
1029 foreach ( $user_payment_tokens as $token => $user_payment_token_data ) {
1030 // Check if token already exists in WC_Payment_Token_CC.
1031 if ( $payment_tokens_handler->user_has_token( $user->id, $token ) ) {
1032 continue;
1033 }
1034
1035 $payment_token = new Square_Credit_Card_Payment_Token();
1036 $payment_token->set_token( $token );
1037 $payment_token->set_card_type( $user_payment_token_data['card_type'] );
1038 $payment_token->set_last4( $user_payment_token_data['last_four'] );
1039 $payment_token->set_expiry_month( $user_payment_token_data['exp_month'] );
1040 $payment_token->set_expiry_year( $user_payment_token_data['exp_year'] );
1041 $payment_token->set_user_id( $user->id );
1042 $payment_token->set_gateway_id( wc_square()->get_gateway()->get_id() );
1043
1044 if ( isset( $user_payment_token_data['nickname'] ) ) {
1045 $payment_token->set_nickname( $user_payment_token_data['nickname'] );
1046 }
1047
1048 $payment_token->save();
1049 }
1050 }
1051 }
1052 }
1053