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 / Settings.php
Settings.php
1,105 lines 27.7 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 use Exception;
27 use WooCommerce\Square\Framework\Square_Helper;
28
29 defined( 'ABSPATH' ) || exit;
30
31 /**
32 * The settings API class.
33 *
34 * This handles registering, getting, and storing the general plugin options.
35 *
36 * Note that this is separate from the gateway settings.
37 *
38 * @since 2.0.0
39 */
40 class Settings extends \WC_Settings_API {
41
42
43 /**
44 * Square Sync setting.
45 *
46 * @var string square Sync setting indicator
47 */
48 const SYSTEM_OF_RECORD_SQUARE = 'square';
49
50 /**
51 * Woocommerce Sync setting.
52 *
53 * @var string square Sync setting indicator
54 */
55 const SYSTEM_OF_RECORD_WOOCOMMERCE = 'woocommerce';
56
57 /**
58 * Disabled Sync setting.
59 *
60 * @var string Sync setting indicator for disabled sync
61 */
62 const SYSTEM_OF_RECORD_DISABLED = 'disabled';
63
64 /** Debug mode log to file */
65 const DEBUG_MODE_LOG = 'log';
66
67 /** Debug mode display on checkout */
68 const DEBUG_MODE_CHECKOUT = 'checkout';
69
70 /** Debug mode log to file and display on checkout */
71 const DEBUG_MODE_BOTH = 'both';
72
73 /** Debug mode disabled */
74 const DEBUG_MODE_OFF = 'off';
75
76 /**
77 * Refresh token
78 *
79 * @var string un-encrypted refresh token
80 */
81 protected $refresh_token;
82
83 /**
84 * Access token
85 *
86 * @var string un-encrypted access token
87 */
88 protected $access_token;
89
90 /**
91 * Square business locations
92 *
93 * @var array business locations returned by the API
94 */
95 protected $locations;
96
97 /**
98 * Square plugin instance
99 *
100 * @var Plugin plugin instance
101 */
102 protected $plugin;
103
104
105 /**
106 * Constructs the class.
107 *
108 * @since 2.0.0
109 *
110 * @param Plugin $plugin plugin instance.
111 */
112 public function __construct( Plugin $plugin ) {
113
114 $this->plugin = $plugin;
115 $this->plugin_id = 'wc_';
116 $this->id = $plugin->get_id();
117
118 add_action( 'init', array( $this, 'init' ) );
119
120 // remove some of our custom fields that shouldn't be saved.
121 add_action(
122 'woocommerce_settings_api_sanitized_fields_' . $this->id,
123 function ( $fields ) {
124
125 unset( $fields['general'], $fields['connect'], $fields['import_products'] );
126
127 if ( $this->is_sandbox() ) {
128 $this->update_access_token( $fields['sandbox_token'] );
129 $this->access_token = false; // Remove encrypted token.
130 $this->refresh_token = false; // Remove encrypted token.
131 }
132
133 // Update the sync interval if it is changed.
134 $this->maybe_change_sync_interval( $fields );
135
136 $this->init_form_fields(); // Reload form fields after saving token.
137
138 return $fields;
139 }
140 );
141
142 add_action( 'admin_notices', array( $this, 'show_auth_keys_changed_notice' ) );
143
144 add_action( 'admin_notices', array( $this, 'show_visit_wizard_notice' ) );
145
146 add_action( 'wp_ajax_wc_square_settings_get_locations', array( $this, 'get_locations_ajax_callback' ) );
147
148 add_action( 'admin_init', array( $this, 'square_onboarding_redirect' ) );
149
150 add_action( 'admin_menu', array( $this, 'register_pages' ) );
151
152 add_action( 'woocommerce_settings_square', array( $this, 'render_square_settings_container' ) );
153
154 add_action( 'woocommerce_settings_checkout', array( $this, 'render_payments_settings_container' ) );
155
156 // Register REST API controllers.
157 new \WooCommerce\Square\Admin\Rest\WC_REST_Square_Settings_Controller();
158 new \WooCommerce\Square\Admin\Rest\WC_REST_Square_Credit_Card_Payment_Settings_Controller();
159 new \WooCommerce\Square\Admin\Rest\WC_REST_Square_Cash_App_Settings_Controller();
160 new \WooCommerce\Square\Admin\Rest\WC_REST_Square_Gift_Cards_Settings_Controller();
161 }
162
163 /**
164 * Redirect users to the templates screen on plugin activation.
165 *
166 * @since 4.7.0
167 */
168 public function square_onboarding_redirect() {
169 if ( ! $this->get_plugin()->get_dependency_handler()->meets_php_dependencies() ) {
170 return;
171 }
172
173 if ( ! get_option( 'wc_square_show_wizard_on_activation' ) ) {
174 add_option( 'wc_square_show_wizard_on_activation', true, '', 'no' );
175 wp_safe_redirect( admin_url( 'admin.php?page=woocommerce-square-onboarding' ) );
176 exit;
177 }
178 }
179
180 /**
181 * Registers square page(s).
182 *
183 * @since 4.7.0
184 */
185 public function register_pages() {
186 if ( ! $this->get_plugin()->get_dependency_handler()->meets_php_dependencies() ) {
187 return;
188 }
189
190 $current_page = isset( $_GET['page'] ) ? wp_unslash( $_GET['page'] ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification.Recommended
191 if ( ! get_option( 'wc_square_connected_page_visited' ) || 'woocommerce-square-onboarding' === $current_page ) {
192 add_submenu_page( 'woocommerce', __( 'Square Onboarding', 'woocommerce-square' ), __( 'Square Onboarding', 'woocommerce-square' ), 'manage_woocommerce', 'woocommerce-square-onboarding', array( $this, 'render_onboarding_page' ) ); // phpcs:ignore WordPress.WP.Capabilities.Unknown
193 }
194 }
195
196 /**
197 * Output the Setup Wizard page(s).
198 */
199 public function render_onboarding_page() {
200 printf(
201 '<div class="wrap" id="woocommerce-square-onboarding"></div>'
202 );
203 }
204
205 /**
206 * Show a notice to visit the wizard on plugin activation.
207 *
208 * @since 4.7.0
209 */
210 public function show_visit_wizard_notice() {
211 if ( ! wc_square()->get_dependency_handler()->meets_php_dependencies() ) {
212 return;
213 }
214
215 if ( get_option( 'wc_square_connected_page_visited' ) ) {
216 return;
217 }
218
219 wc_square()->get_admin_notice_handler()->add_admin_notice(
220 sprintf(
221 /* translators: %1$s - <a> tag, %2$s - </a> tag */
222 esc_html__(
223 'Welcome to Square for WooCommerce! Get started by visiting the %1$sOnboarding Wizard%2$s.',
224 'woocommerce-square'
225 ),
226 '<a href="' . esc_url( admin_url( 'admin.php?page=woocommerce-square-onboarding' ) ) . '">',
227 '</a>'
228 ),
229 'wc-square-welcome',
230 array(
231 'dismissible' => false,
232 'notice_class' => 'notice-info',
233 )
234 );
235 }
236
237 /**
238 * Redirect users to the onboarding wizard screen on plugin activation.
239 *
240 * @since 4.7.0
241 */
242 public function render_square_settings_container() {
243 $section = isset( $_GET['section'] ) && ! empty( $_GET['section'] ) ? wp_unslash( $_GET['section'] ) : 'general'; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification.Recommended
244
245 printf(
246 '<div id="woocommerce-square-settings__container-' . esc_html( $section ) . '"></div>',
247 );
248 }
249
250 /**
251 * Redirect users to the onboarding wizard screen on plugin activation.
252 *
253 * @since 4.7.0
254 */
255 public function render_payments_settings_container() {
256 $tab = isset( $_GET['tab'] ) ? wp_unslash( $_GET['tab'] ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification.Recommended
257 $section = isset( $_GET['section'] ) ? wp_unslash( $_GET['section'] ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification.Recommended
258
259 if ( 'checkout' !== $tab ) {
260 return;
261 }
262
263 if ( ! ( 'square_credit_card' === $section || 'square_cash_app_pay' === $section || 'gift_cards_pay' === $section ) ) {
264 return;
265 }
266
267 printf(
268 '<div id="woocommerce-square-payment-gateway-settings__container--' . esc_html( $section ) . '"></div>',
269 );
270 }
271
272 /**
273 * Show warning to reconnect if the `SQUARE_ENCRYPTION_KEY` and `SQUARE_ENCRYPTION_SALT` constants
274 * are newly added.
275 *
276 * @since 4.2.0
277 */
278 public function show_auth_keys_changed_notice() {
279 $is_keys_updated = get_option( 'wc_square_auth_key_updated', false );
280 $show_message = ( $this->is_custom_square_auth_keys_set() && empty( $is_keys_updated ) )
281 || ( ! $this->is_custom_square_auth_keys_set() && $is_keys_updated );
282
283 if ( $show_message ) {
284 wc_square()->get_admin_notice_handler()->add_admin_notice(
285 esc_html__( 'Square was disconnected because authentication keys were changed. Please connect again.', 'woocommerce-square' ),
286 'wc-square-disconnected-keys-changed',
287 array(
288 'dismissible' => false,
289 'notice_class' => 'notice-warning',
290 )
291 );
292
293 delete_option( 'wc_square_access_tokens' );
294 }
295
296 if ( ! $this->is_custom_square_auth_keys_set() && $is_keys_updated ) {
297 delete_option( 'wc_square_auth_key_updated' );
298 }
299 }
300
301 /**
302 * Returns true if `SQUARE_ENCRYPTION_KEY` and `SQUARE_ENCRYPTION_SALT` constants are both set.
303 *
304 * @since 4.2.0
305 *
306 * @return boolean
307 */
308 public function is_custom_square_auth_keys_set() {
309 return defined( 'SQUARE_ENCRYPTION_KEY' ) && defined( 'SQUARE_ENCRYPTION_SALT' );
310 }
311
312 /**
313 * Initializes form fields and settings.
314 *
315 * @since 3.5.1
316 */
317 public function init() {
318 $this->init_form_fields();
319 $this->init_settings();
320 }
321
322
323 /**
324 * Initializes the form fields.
325 *
326 * @since 2.0.0
327 */
328 public function init_form_fields() {
329 $this->form_fields = array();
330 }
331
332
333 /**
334 * Gets the form fields.
335 *
336 * Overridden to populate the Location settings options on display.
337 *
338 * @since 2.0.0
339 *
340 * @return array
341 */
342 public function get_form_fields() {
343
344 $fields = parent::get_form_fields();
345
346 // Confirm our local enable sandbox setting matches what is sent from the front end
347 // to account for changes from sandbox to production incorrectly fetching sandbox locations.
348 if ( $this->settings && isset( $_POST['wc_square_environment'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
349
350 $environment = 'yes' === $this->settings['enable_sandbox'] ? 'sandbox' : 'production';
351
352 if ( $environment !== $_POST['wc_square_environment'] ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
353 return $fields;
354 }
355 }
356
357 $location_id_field_key = '';
358 // Get the location_id field.
359 foreach ( $fields as $key => $value ) {
360 if ( strpos( $key, 'location_id' ) ) {
361 $location_id_field_key = $key;
362 break;
363 }
364 }
365
366 if ( did_action( 'wc_square_initialized' ) && $this->is_admin_settings_screen() && ! empty( $location_id_field_key ) ) {
367
368 $locations = array(
369 '' => __( 'Please choose a location', 'woocommerce-square' ),
370 );
371
372 if ( ! empty( $this->get_locations() ) ) {
373 foreach ( $this->get_locations() as $location ) {
374 if ( 'ACTIVE' === $location->getStatus() && in_array( 'CREDIT_CARD_PROCESSING', (array) $location->getCapabilities(), true ) ) {
375 $locations[ $location->getId() ] = $location->getName();
376 }
377 }
378 }
379
380 $fields[ $location_id_field_key ]['options'] = $locations;
381 }
382
383 return $fields;
384 }
385
386
387 /**
388 * Generates the HTML for import products button.
389 *
390 * @param string $id form id.
391 * @param array $field form fields.
392 */
393 public function generate_import_products_html( $id, $field ) {
394
395 $is_location_set = (bool) $this->get_location_id();
396 $is_sor_set = (bool) $this->get_system_of_record_name();
397 $display = $is_location_set && $is_sor_set ? '' : 'display: none';
398
399 ob_start();
400 ?>
401 <tr valign="top" style="<?php echo esc_attr( $display ); ?>">
402 <th scope="row" class="titledesc">
403 <label for="<?php echo esc_attr( $id ); ?>"><?php echo wp_kses_post( $field['title'] ); ?> <?php echo $this->get_tooltip_html( $field ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></label>
404 </th>
405 <td class="forminp">
406 <a id="wc_square_import_products" href='#' class='button js-import-square-products <?php echo ( ! $this->get_location_id() ? 'disabled' : '' ); ?>'>
407 <?php echo esc_html__( 'Import all products from Square', 'woocommerce-square' ); ?>
408 </a>
409 <p class="description wc_square_save_changes_message" style="display: none;"><?php esc_html_e( 'You have made changes to the settings. Please save the changes to enable the button.', 'woocommerce-square' ); ?></p>
410 </td>
411 </tr>
412 <?php
413
414 return ob_get_clean();
415 }
416
417
418 /**
419 * Generates the Connection field HTML.
420 *
421 * @since 2.0.0
422 *
423 * @param string $id field ID.
424 * @param array $field field data.
425 * @return string
426 */
427 public function generate_connect_html( $id, $field ) {
428
429 ob_start();
430 ?>
431 <tr valign="top">
432 <th scope="row" class="titledesc">
433 <label for="<?php echo esc_attr( $id ); ?>"><?php echo wp_kses_post( $field['title'] ); ?> <?php echo $this->get_tooltip_html( $field ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></label>
434 </th>
435 <td class="forminp">
436 <?php
437 if ( $this->get_access_token() ) {
438 echo $this->get_plugin()->get_connection_handler()->get_disconnect_button_html(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
439 } else {
440 echo $this->get_plugin()->get_connection_handler()->get_connect_button_html( $this->is_sandbox() ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
441 }
442 ?>
443 </td>
444 </tr>
445 <?php
446
447 return ob_get_clean();
448 }
449
450
451 /**
452 * Updates the stored refresh token.
453 *
454 * @since 2.0.0
455 *
456 * @param string $token refresh token.
457 */
458 public function update_refresh_token( $token ) {
459
460 $refresh_tokens = $this->get_refresh_tokens();
461 $environment = $this->get_environment();
462
463 if ( ! empty( $token ) ) {
464
465 $this->refresh_token = $token;
466
467 if ( Utilities\Encryption_Utility::is_encryption_supported() ) {
468
469 $encryption = new Utilities\Encryption_Utility();
470
471 try {
472
473 $token = $encryption->encrypt_data( $token );
474
475 } catch ( \Exception $exception ) {
476
477 // log the event, but don't halt the process.
478 $this->get_plugin()->log( 'Could not encrypt refresh token. ' . $exception->getMessage() );
479 }
480 }
481
482 $refresh_tokens[ $environment ] = $token;
483 }
484
485 update_option( 'wc_square_refresh_tokens', $refresh_tokens );
486 }
487
488
489 /**
490 * Updates the stored access token.
491 *
492 * @since 2.0.0
493 *
494 * @param string $token access token.
495 */
496 public function update_access_token( $token ) {
497
498 $access_tokens = $this->get_access_tokens();
499 $environment = $this->get_environment();
500
501 if ( ! empty( $token ) ) {
502
503 $this->access_token = $token;
504
505 if ( Utilities\Encryption_Utility::is_encryption_supported() ) {
506
507 $encryption = new Utilities\Encryption_Utility();
508
509 try {
510
511 $token = $encryption->encrypt_data( $token );
512
513 } catch ( \Exception $exception ) {
514
515 // log the event, but don't halt the process.
516 $this->get_plugin()->log( 'Could not encrypt access token. ' . $exception->getMessage() );
517 }
518 }
519
520 $access_tokens[ $environment ] = $token;
521 } elseif ( isset( $access_tokens[ $environment ] ) ) {
522
523 unset( $access_tokens[ $environment ] );
524 }
525
526 update_option( 'wc_square_access_tokens', $access_tokens );
527 }
528
529
530 /**
531 * Clears any stored refresh tokens.
532 *
533 * @since 2.0.0
534 */
535 public function clear_refresh_tokens() {
536 delete_option( 'wc_square_refresh_tokens' );
537 }
538
539
540 /**
541 * Clears any stored access tokens.
542 *
543 * @since 2.0.0
544 */
545 public function clear_access_tokens() {
546
547 delete_option( 'wc_square_access_tokens' );
548 }
549
550
551 /**
552 * Clears the location ID from the settings.
553 *
554 * This is helpful on disconnect / revoke so that previously set location IDs don't stick around and cause confusion.
555 *
556 * @since 2.0.0
557 */
558 public function clear_location_id() {
559
560 $settings = get_option( $this->get_option_key(), array() );
561
562 $settings[ $this->get_environment() . '_location_id' ] = '';
563
564 update_option( $this->get_option_key(), $settings );
565 }
566
567
568 /** Conditional methods *******************************************************************************************/
569
570
571 /**
572 * Determines if WooCommerce is configured to be the Sync setting.
573 *
574 * @since 2.0.0
575 *
576 * @return bool
577 */
578 public function is_system_of_record_woocommerce() {
579
580 return self::SYSTEM_OF_RECORD_WOOCOMMERCE === $this->get_system_of_record();
581 }
582
583
584 /**
585 * Determines if Square is configured to be the Sync setting.
586 *
587 * @since 2.0.0
588 *
589 * @return bool
590 */
591 public function is_system_of_record_square() {
592
593 return self::SYSTEM_OF_RECORD_SQUARE === $this->get_system_of_record();
594 }
595
596
597 /**
598 * Determines if there is no Sync setting.
599 *
600 * @since 2.0.0
601 *
602 * @return bool
603 */
604 public function is_system_of_record_disabled() {
605
606 $sor = $this->get_system_of_record();
607
608 return empty( $sor ) || self::SYSTEM_OF_RECORD_DISABLED === $sor;
609 }
610
611
612 /**
613 * Determines if inventory sync is enabled.
614 *
615 * @since 2.0.0
616 *
617 * @return bool
618 */
619 public function is_inventory_sync_enabled() {
620
621 /**
622 * Filters the inventory sync setting.
623 *
624 * @since 2.0.0
625 */
626 return (bool) apply_filters( 'wc_square_inventory_sync_enabled', 'yes' === get_option( 'woocommerce_manage_stock' ) && $this->is_product_sync_enabled() && 'yes' === $this->get_option( 'enable_inventory_sync' ) );
627 }
628
629 /**
630 * Determines if image overriding is enabled.
631 *
632 * @since 3.9.0
633 *
634 * @return bool
635 */
636 public function is_override_product_images_enabled() {
637 /**
638 * Filter to enable/disable overriding product images.
639 *
640 * @since 3.9.0
641 *
642 * @param boolean 'should_override' Boolean flag to toggle overriding image feature.
643 */
644 return (bool) apply_filters( 'wc_square_override_product_images_enabled', 'yes' === $this->get_option( 'override_product_images' ) );
645 }
646
647 /**
648 * Determines if order fulfillment sync is enabled.
649 *
650 * @since 5.0.0
651 *
652 * @return bool
653 */
654 public function is_order_fulfillment_sync_enabled() {
655 /**
656 * Filter to enable/disable order fulfillment sync.
657 *
658 * @since 5.0.0
659 *
660 * @param boolean 'should_sync' Boolean flag to toggle order fulfillment sync feature.
661 */
662 return (bool) apply_filters( 'wc_square_order_fulfillment_sync_enabled', 'yes' === $this->get_option( 'enable_order_fulfillment_sync' ) );
663 }
664
665
666 /**
667 * Determines if product sync is enabled.
668 *
669 * @since 2.0.0
670 *
671 * @return bool
672 */
673 public function is_product_sync_enabled() {
674
675 return ! $this->is_system_of_record_disabled();
676 }
677
678
679 /**
680 * Determines whether to hide products that don't exist in square from the catalog.
681 *
682 * @since 2.0.0
683 *
684 * @return bool
685 */
686 public function hide_missing_square_products() {
687
688 return 'yes' === $this->get_option( 'hide_missing_products' );
689 }
690
691 /**
692 * Returns sync interval in seconds.
693 * Returns 1 hr = 3600 seconds as default.
694 *
695 * @since 3.5.1
696 *
697 * @return int
698 */
699 public function get_sync_interval() {
700 $sync_interval = $this->get_option( 'sync_interval', '' );
701 $sync_interval = empty( $sync_interval ) ? HOUR_IN_SECONDS : $sync_interval * HOUR_IN_SECONDS;
702
703 /**
704 * Filters the frequency with which products should be synced.
705 *
706 * @since 2.0.0
707 *
708 * @param int $interval sync interval in seconds (defaults to one hour)
709 */
710 return (int) max( MINUTE_IN_SECONDS, (int) apply_filters( 'wc_square_sync_interval', $sync_interval ) );
711 }
712
713
714 /**
715 * Determines if the plugin settings are fully configured.
716 *
717 * @since 2.0.0
718 *
719 * @return bool
720 */
721 public function is_configured() {
722
723 return $this->get_location_id() && $this->get_system_of_record();
724 }
725
726
727 /**
728 * Determines if the plugin is connected to Square.
729 *
730 * @since 2.0.0
731 *
732 * @return bool
733 */
734 public function is_connected() {
735
736 return (bool) $this->get_access_token();
737 }
738
739
740 /**
741 * Determines if configured in the sandbox environment.
742 *
743 * @since 2.0.0
744 *
745 * @return bool
746 */
747 public function is_sandbox() {
748
749 return 'sandbox' === $this->get_environment();
750 }
751
752
753 /**
754 * Determines if debug logging is enabled.
755 *
756 * @since 2.0.0
757 *
758 * @return bool
759 */
760 public function is_debug_enabled() {
761
762 return 'yes' === $this->get_option( 'debug_logging_enabled' );
763 }
764
765
766 /** Getter methods ************************************************************************************************/
767
768
769 /**
770 * Gets the configured location.
771 *
772 * @since 2.0.0
773 *
774 * @return string
775 */
776 public function get_location_id() {
777 $location_id = $this->get_option( $this->get_environment() . '_location_id' );
778
779 if ( empty( $location_id ) ) {
780 $square_db_version = get_option( $this->get_plugin()->get_plugin_version_name() );
781
782 // if the Square DB version is still pre-2.2.0, fetch the location ID using the previous option name
783 if ( ! empty( $square_db_version ) && version_compare( $square_db_version, '2.2.0', '<' ) ) {
784 $location_id = $this->get_option( 'location_id' );
785 }
786 }
787
788 return $location_id;
789 }
790
791
792 /**
793 * Gets the available locations.
794 *
795 * @since 2.0.0
796 *
797 * @param bool $force whether to force a refetch of the locations.
798 *
799 * @return \Square\Models\Location[]
800 */
801 public function get_locations( $force = false ) {
802 if ( is_array( $this->locations ) ) {
803 return $this->locations;
804 }
805
806 $locations_transient_key = 'wc_square_locations_' . $this->get_plugin()->get_version();
807
808 $section = isset( $_GET['section'] ) ? sanitize_text_field( wp_unslash( $_GET['section'] ) ) : false; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
809
810 if ( ! ( ( $this->is_admin_settings_screen() && 'update' !== $section ) || $force ) ) {
811 $this->locations = get_transient( $locations_transient_key );
812 }
813
814 if ( ! is_array( $this->locations ) && did_action( 'wc_square_initialized' ) ) {
815
816 $this->locations = array();
817
818 if ( ! $this->get_plugin()->get_dependency_handler()->meets_php_dependencies() ) {
819 return $this->locations;
820 }
821
822 try {
823
824 // cache the locations returned so they can be used elsewhere.
825 $this->locations = $this->get_plugin()->get_api( $this->get_access_token(), $this->is_sandbox() )->get_locations();
826 set_transient( $locations_transient_key, $this->locations, HOUR_IN_SECONDS );
827
828 // check the returned IDs against what's currently configured.
829 $stored_location_id = $this->get_location_id();
830 $found = ! $stored_location_id;
831
832 foreach ( $this->locations as $location ) {
833
834 if ( $stored_location_id && $location->getId() === $stored_location_id ) {
835 $found = true;
836 break;
837 }
838 }
839
840 // if the currently set location ID is not present in the connected account's available locations, clear it locally.
841 if ( ! $found ) {
842 $this->clear_location_id();
843 }
844 } catch ( \Exception $exception ) {
845 $this->get_plugin()->log( 'Could not retrieve business locations.' );
846 }
847 }
848
849 return $this->locations;
850 }
851
852 /**
853 * Ajax callback for locations.
854 *
855 * @since 4.7.0
856 */
857 public function get_locations_ajax_callback() {
858 check_ajax_referer( 'wc_square_settings', 'security' );
859
860 $locations = $this->get_locations( true );
861
862 wp_send_json_success( $locations );
863 }
864
865 /**
866 * Gets the configured Sync setting.
867 *
868 * @since 2.0.0
869 *
870 * @return string
871 */
872 public function get_system_of_record() {
873
874 return $this->get_option( 'system_of_record' );
875 }
876
877
878 /**
879 * Gets the configured Sync setting name.
880 *
881 * @since 2.0.0
882 *
883 * @return string or empty string if no Sync setting is configured
884 */
885 public function get_system_of_record_name() {
886
887 switch ( $this->get_system_of_record() ) {
888
889 case 'square':
890 $sor = __( 'Square', 'woocommerce-square' );
891 break;
892 case 'woocommerce':
893 $sor = __( 'WooCommerce', 'woocommerce-square' );
894 break;
895 default:
896 $sor = '';
897 break;
898 }
899
900 return $sor;
901 }
902
903 /**
904 * Gets the refresh token.
905 *
906 * @since 2.0.0
907 *
908 * @return string|null
909 */
910 public function get_refresh_token() {
911
912 if ( empty( $this->refresh_token ) ) {
913
914 $tokens = $this->get_refresh_tokens();
915 $token = null;
916
917 if ( ! empty( $tokens[ $this->get_environment() ] ) ) {
918 $token = $tokens[ $this->get_environment() ];
919 }
920
921 if ( $token && Utilities\Encryption_Utility::is_encryption_supported() ) {
922
923 $encryption = new Utilities\Encryption_Utility();
924
925 try {
926
927 $token = $encryption->decrypt_data( $token );
928
929 } catch ( \Exception $exception ) {
930
931 // log the event, but don't halt the process.
932 $this->get_plugin()->log( 'Could not decrypt refresh token. ' . $exception->getMessage() );
933 }
934 }
935
936 $this->refresh_token = $token;
937 }
938
939 /**
940 * Filters the configured refresh token.
941 *
942 * @since 2.0.0
943 *
944 * @param string $refresh_token
945 */
946 return apply_filters( 'wc_square_refresh_token', $this->refresh_token );
947 }
948
949 /**
950 * Gets the access token.
951 *
952 * @since 2.0.0
953 *
954 * @return string|null
955 */
956 public function get_access_token() {
957
958 if ( empty( $this->access_token ) || $this->is_admin_settings_screen() ) {
959
960 $tokens = $this->get_access_tokens();
961 $token = null;
962
963 if ( ! empty( $tokens[ $this->get_environment() ] ) ) {
964 $token = $tokens[ $this->get_environment() ];
965 }
966
967 if ( $token && Utilities\Encryption_Utility::is_encryption_supported() ) {
968
969 $encryption = new Utilities\Encryption_Utility();
970
971 try {
972
973 $token = $encryption->decrypt_data( $token );
974
975 } catch ( \Exception $exception ) {
976
977 // log the event, but don't halt the process.
978 $this->get_plugin()->log( 'Could not decrypt access token. ' . $exception->getMessage() );
979 }
980 }
981
982 $this->access_token = $token;
983 }
984
985 /**
986 * Filters the configured access token.
987 *
988 * @since 2.0.0
989 *
990 * @param string $access_token access token
991 */
992 return apply_filters( 'wc_square_access_token', $this->access_token );
993 }
994
995
996 /**
997 * Gets the stored access tokens.
998 *
999 * Each environment may have its own token.
1000 *
1001 * @since 2.0.0
1002 *
1003 * @return array
1004 */
1005 public function get_access_tokens() {
1006 return (array) get_option( 'wc_square_access_tokens', array() );
1007 }
1008
1009
1010 /**
1011 * Gets the stored refresh tokens.
1012 *
1013 * Each environment may have its own token.
1014 *
1015 * @since 2.0.0
1016 *
1017 * @return array
1018 */
1019 public function get_refresh_tokens() {
1020 return (array) get_option( 'wc_square_refresh_tokens', array() );
1021 }
1022
1023 /**
1024 * Gets setting enabled sandbox.
1025 *
1026 * @since 2.1.2
1027 *
1028 * @return string
1029 */
1030 public function get_enable_sandbox() {
1031 return $this->get_option( 'enable_sandbox' );
1032 }
1033
1034 /**
1035 * Tells is if the setting for enabling sandbox is checked.
1036 *
1037 * @since 2.1.2
1038 *
1039 * @return boolean
1040 */
1041 public function is_sandbox_setting_enabled() {
1042 return 'yes' === $this->get_enable_sandbox();
1043 }
1044
1045
1046 /**
1047 * Gets the configured environment.
1048 *
1049 * @since 2.0.0
1050 *
1051 * @return string
1052 */
1053 public function get_environment() {
1054 $sanboxed = ( defined( 'WC_SQUARE_SANDBOX' ) && WC_SQUARE_SANDBOX ) || $this->is_sandbox_setting_enabled();
1055 return $sanboxed ? 'sandbox' : 'production';
1056 }
1057
1058
1059 /**
1060 * Gets the plugin instance.
1061 *
1062 * @since 2.0.0
1063 *
1064 * @return Plugin
1065 */
1066 public function get_plugin() {
1067
1068 return $this->plugin;
1069 }
1070
1071 /**
1072 * Determines if the current request is for the Square admin settings screen.
1073 *
1074 * @since 2.1.5
1075 * @return bool True if the current request is for the Square admin settings, otherwise false.
1076 */
1077 public function is_admin_settings_screen() {
1078 return isset( $_GET['page'], $_GET['tab'] ) && 'wc-settings' === $_GET['page'] && Plugin::PLUGIN_ID === $_GET['tab']; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
1079 }
1080
1081 /**
1082 * Update the sync interval if it has changed.
1083 *
1084 * @param array $settings
1085 * @return void
1086 */
1087 public function maybe_change_sync_interval( $settings ) {
1088 // Bail if we have a filter in place to manage the sync interval.
1089 if ( has_filter( 'wc_square_sync_interval' ) ) {
1090 return;
1091 }
1092
1093 $old_settings = get_option( $this->get_option_key(), array() );
1094 // Bail if we don't have a sync interval.
1095 if ( empty( $old_settings['sync_interval'] ) || empty( $settings['sync_interval'] ) ) {
1096 return;
1097 }
1098
1099 // If the sync interval has changed, schedule a new sync.
1100 if ( $old_settings['sync_interval'] !== $settings['sync_interval'] ) {
1101 $this->plugin->get_sync_handler()->schedule_sync( true );
1102 }
1103 }
1104 }
1105