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