PluginProbe ʕ •ᴥ•ʔ
WooCommerce Square / 3.8.1
WooCommerce Square v3.8.1
5.4.2 5.4.1 5.4.0 trunk 1.0.25 1.0.26 1.0.27 1.0.28 1.0.29 1.0.30 1.0.31 1.0.32 1.0.33 1.0.34 1.0.35 1.0.36 1.0.37 1.0.38 2.0.0 2.0.1 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.0.8 2.1.0 2.1.1 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.2.0 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.3.0 2.3.1 2.3.2 2.3.3 2.3.4 2.4.0 2.4.1 2.5.0 2.5.1 2.5.2 2.5.3 2.6.0 2.7.0 2.8.0 2.9.0 2.9.1 3.0.0 3.0.1 3.0.2 3.0.3 3.1.0 3.2.0 3.3.0 3.4.0 3.4.1 3.4.2 3.5.0 3.6.0 3.6.1 3.7.0 3.7.1 3.8.0 3.8.1 3.8.2 3.8.3 3.9.0 4.0.0 4.1.0 4.2.0 4.2.1 4.2.2 4.2.3 4.3.0 4.3.1 4.3.2 4.4.0 4.4.1 4.4.2 4.5.0 4.5.1 4.5.2 4.6.0 4.6.1 4.6.2 4.6.3 4.6.4 4.7.0 4.7.1 4.7.2 4.7.3 4.7.4 4.8.0 4.8.1 4.8.2 4.8.3 4.8.4 4.8.5 4.8.6 4.8.7 4.8.8 4.9.0 4.9.1 4.9.2 4.9.3 4.9.4 4.9.5 4.9.6 4.9.7 4.9.8 4.9.9 5.0.0 5.0.1 5.1.0 5.1.1 5.1.2 5.2.0 5.3.0 5.3.1 5.3.2 5.3.3
woocommerce-square / includes / Handlers / Connection.php
woocommerce-square / includes / Handlers Last commit date
Product 3 years ago Background_Job.php 3 years ago Category.php 3 years ago Connection.php 3 years ago Email.php 3 years ago Order.php 3 years ago Product.php 3 years ago Products.php 3 years ago Sync.php 3 years ago
Connection.php
531 lines
1 <?php
2 /**
3 * WooCommerce Square
4 *
5 * This source file is subject to the GNU General Public License v3.0
6 * that is bundled with this package in the file license.txt.
7 * It is also available through the world-wide-web at this URL:
8 * http://www.gnu.org/licenses/gpl-3.0.html GNU General Public License v3.0 or later
9 * If you did not receive a copy of the license and are unable to
10 * obtain it through the world-wide-web, please send an email
11 * to license@woocommerce.com so we can send you a copy immediately.
12 *
13 * DISCLAIMER
14 *
15 * Do not edit or add to this file if you wish to upgrade WooCommerce Square to newer
16 * versions in the future. If you wish to customize WooCommerce Square for your
17 * needs please refer to https://docs.woocommerce.com/document/woocommerce-square/
18 *
19 * @author WooCommerce
20 * @copyright Copyright: (c) 2019, Automattic, Inc.
21 * @license http://www.gnu.org/licenses/gpl-3.0.html GNU General Public License v3.0 or later
22 */
23
24 namespace WooCommerce\Square\Handlers;
25
26 use Square\Models\ListCustomersResponse;
27 use WooCommerce\Square;
28
29 defined( 'ABSPATH' ) || exit;
30
31 /**
32 * The admin connection handler.
33 *
34 * @since 2.0.0
35 */
36 class Connection {
37
38
39 /** @var string production connect URL */
40 const CONNECT_URL_PRODUCTION = 'https://connect.woocommerce.com/login/square';
41
42 /** @var string sandbox connect URL */
43 const CONNECT_URL_SANDBOX = 'https://connect.woocommerce.com/login/squaresandbox';
44
45 /** @var string production refresh URL */
46 const REFRESH_URL_PRODUCTION = 'https://connect.woocommerce.com/renew/square';
47
48 /** @var string sandbox refresh URL */
49 const REFRESH_URL_SANDBOX = 'https://connect.woocommerce.com/renew/squaresandbox';
50
51 /** @var Square\Plugin plugin instance */
52 protected $plugin;
53
54
55 /**
56 * Constructs the class.
57 *
58 * @since 2.0.0
59 *
60 * @param Square\Plugin $plugin plugin instance
61 */
62 public function __construct( Square\Plugin $plugin ) {
63
64 $this->plugin = $plugin;
65
66 $this->add_hooks();
67 }
68
69
70 /**
71 * Adds the action and filter hooks.
72 *
73 * @since 2.0.0
74 */
75 protected function add_hooks() {
76
77 add_action( 'admin_action_wc_square_connected', array( $this, 'handle_connected' ) );
78
79 add_action( 'admin_action_wc_square_disconnect', array( $this, 'handle_disconnect' ) );
80
81 // refresh the connection, triggered by Action Scheduler
82 add_action( 'wc_square_refresh_connection', array( $this, 'refresh_connection' ) );
83
84 // index customers, triggered by Action Scheduler
85 add_action( 'wc_square_index_customers', array( $this, 'index_customers' ) );
86 }
87
88
89 /**
90 * Handles a successful connection.
91 *
92 * @internal
93 *
94 * @since 2.0.0
95 */
96 public function handle_connected() {
97
98 // phpcs:ignore WordPress.Security.NonceVerification.Recommended - Setting variable, nonce checked next line.
99 $nonce = isset( $_GET['_wpnonce'] ) ? wc_clean( wp_unslash( $_GET['_wpnonce'] ) ) : '';
100
101 // check the user role & nonce
102 if ( ! current_user_can( 'manage_woocommerce' ) || ! wp_verify_nonce( $nonce, 'wc_square_connected' ) ) {
103 wp_die( esc_html__( 'Sorry, you do not have permission to manage the Square connection.', 'woocommerce-square' ) );
104 }
105
106 $access_token = ! empty( $_GET['square_access_token'] ) ? sanitize_text_field( urldecode( $_GET['square_access_token'] ) ) : '';
107
108 if ( empty( $access_token ) ) {
109 $this->get_plugin()->log( 'Error: No access token was received.' );
110 add_action(
111 'admin_notices',
112 function () {
113 ?>
114 <div class="notice notice-error is-dismissible">
115 <p><?php esc_html_e( 'Square Error: We could not connect to Square. No access token was given.!', 'woocommerce-square' ); ?></p>
116 </div>
117 <?php
118 }
119 );
120 return;
121 }
122
123 $this->get_plugin()->get_settings_handler()->update_access_token( $access_token );
124 $this->get_plugin()->log( 'Access token successfully received.' );
125
126 $refresh_token = ! empty( $_GET['square_refresh_token'] ) ? sanitize_text_field( urldecode( $_GET['square_refresh_token'] ) ) : '';
127 if ( empty( $refresh_token ) ) {
128 $this->get_plugin()->log( 'Failed to receive refresh token from connect server.' );
129 } else {
130 $this->get_plugin()->get_settings_handler()->update_refresh_token( $refresh_token );
131 $this->get_plugin()->log( 'Refresh token successfully received.' );
132 }
133
134 $this->schedule_refresh();
135 $this->schedule_customer_index();
136
137 // on connect after upgrading to v2.0 from v1.0, initiate a catalog sync to refresh the Square item IDs
138 if ( get_option( 'wc_square_updated_to_2_0_0' ) ) {
139
140 // delete any old access token from v1, as it will be invalidated
141 delete_option( 'woocommerce_square_merchant_access_token' );
142
143 if ( $this->get_plugin()->get_settings_handler()->is_system_of_record_square() ) {
144 $this->get_plugin()->get_sync_handler()->start_manual_sync();
145 }
146 }
147
148 delete_option( 'wc_square_updated_to_2_0_0' );
149
150 wp_safe_redirect( $this->get_plugin()->get_settings_url() );
151 exit;
152 }
153
154
155 /**
156 * Handles disconnection.
157 *
158 * @internal
159 *
160 * @since 2.0.0
161 */
162 public function handle_disconnect() {
163
164 // remove the refresh fail flag if previously set
165 delete_option( 'wc_square_refresh_failed' );
166
167 // phpcs:ignore WordPress.Security.NonceVerification.Recommended - Setting var, nonce checked next line.
168 $nonce = isset( $_GET['_wpnonce'] ) ? wc_clean( $_GET['_wpnonce'] ) : '';
169
170 // check the user role & nonce
171 if ( ! current_user_can( 'manage_woocommerce' ) || ! wp_verify_nonce( $nonce, 'wc_square_disconnect' ) ) {
172 wp_die( esc_html__( 'Sorry, you do not have permission to manage the Square connection.', 'woocommerce-square' ) );
173 }
174
175 // disconnect by clearing tokens, unscheduling syncs, etc...
176 $this->disconnect();
177
178 $this->get_plugin()->log( 'Manually disconnected' );
179
180 $this->get_plugin()->get_message_handler()->add_message( __( 'Disconnected successfully', 'woocommerce-square' ) );
181
182 wp_safe_redirect( $this->get_plugin()->get_settings_url() );
183 exit;
184 }
185
186
187 /**
188 * Disconnects the plugin.
189 *
190 * @since 2.0.0
191 */
192 public function disconnect() {
193
194 // don't try to refresh anymore
195 $this->unschedule_refresh();
196
197 // unschedule the interval sync
198 $this->get_plugin()->get_sync_handler()->unschedule_sync();
199
200 // fully clear the access token
201 $this->get_plugin()->get_settings_handler()->clear_access_tokens();
202 $this->get_plugin()->get_settings_handler()->clear_refresh_tokens();
203
204 // clear all background jobs so further API requests aren't attempted
205 $this->get_plugin()->get_background_job_handler()->clear_all_jobs();
206 }
207
208
209 /** Refresh methods ***********************************************************************************************/
210
211
212 /**
213 * Schedules the connection refresh.
214 *
215 * @since 2.0.0
216 */
217 public function schedule_refresh() {
218
219 if ( ! $this->get_plugin()->get_settings_handler()->is_connected() ) {
220 return;
221 }
222
223 /**
224 * Filters the frequency with which the OAuth connection should be refreshed.
225 *
226 * @since 2.0.0
227 *
228 * @param int $interval refresh interval
229 */
230 $interval = apply_filters( 'wc_square_connection_refresh_interval', WEEK_IN_SECONDS );
231
232 // Make sure that all refresh actions are cancelled before scheduling it.
233 $this->unschedule_refresh();
234
235 as_schedule_single_action( time() + $interval, 'wc_square_refresh_connection', array(), 'square' );
236 }
237
238
239 /**
240 * Refreshes the access token via the Woo proxy.
241 *
242 * @since 2.0.0
243 */
244 public function refresh_connection() {
245 if ( $this->get_plugin()->get_settings_handler()->is_sandbox() ) {
246 return;
247 }
248
249 try {
250
251 if ( $this->get_plugin()->get_settings_handler()->is_debug_enabled() ) {
252 $this->get_plugin()->log( 'Refreshing connection...' );
253 }
254
255 $refresh_token = $this->get_plugin()->get_settings_handler()->get_refresh_token();
256
257 if ( ! $refresh_token ) {
258 $this->get_plugin()->log( 'No refresh token stored, cannot refresh connection.' );
259 update_option( 'wc_square_refresh_failed', 'yes' );
260 wc_square()->get_email_handler()->get_access_token_email()->trigger();
261 return;
262 }
263
264 $request = array(
265 'body' => array(
266 'token' => $this->get_plugin()->get_settings_handler()->get_refresh_token(),
267 ),
268 'timeout' => 45,
269 );
270
271 // make the request
272 $response = wp_remote_post( $this->get_refresh_url(), $request );
273
274 // handle HTTP errors
275 if ( is_wp_error( $response ) ) {
276 throw new \Exception( $response->get_error_message() );
277 }
278
279 $response = new Square\API\Responses\Connection_Refresh_Response( wp_remote_retrieve_body( $response ) );
280
281 // check for errors in the response
282 if ( $response->has_error() ) {
283 throw new \Exception( $response->get_error_message() );
284 }
285
286 // ensure an access token, just in case
287 if ( ! $response->get_token() ) {
288 throw new \Exception( 'Access token missing from the response' );
289 }
290
291 // store the new token
292 $this->get_plugin()->get_settings_handler()->update_access_token( $response->get_token() );
293
294 // In case square updates the refresh token.
295 if ( $response->get_refresh_token() ) {
296 $this->get_plugin()->get_settings_handler()->update_refresh_token( $response->get_refresh_token() );
297 $this->get_plugin()->log( 'Connection successfully refreshed.' );
298 }
299
300 // in case this option was set
301 delete_option( 'wc_square_refresh_failed' );
302 } catch ( \Exception $exception ) {
303
304 $this->get_plugin()->log( 'Unable to refresh connection: ' . $exception->getMessage() );
305
306 update_option( 'wc_square_refresh_failed', 'yes' );
307
308 wc_square()->get_email_handler()->get_access_token_email()->trigger();
309 }
310
311 $this->schedule_refresh();
312 }
313
314
315 /**
316 * Unschedules the connection refresh.
317 *
318 * @since 2.0.0
319 */
320 protected function unschedule_refresh() {
321 as_unschedule_all_actions( 'wc_square_refresh_connection', array(), 'square' );
322 }
323
324
325 /** Customer index methods ****************************************************************************************/
326
327
328 /**
329 * Index existing Square customers.
330 *
331 * @since 2.0.0
332 *
333 * @param string $cursor pagination cursor
334 */
335 public function index_customers( $cursor = '' ) {
336
337 try {
338
339 $response = $this->get_plugin()->get_api()->get_customers( $cursor );
340
341 if ( $response->get_data() instanceof ListCustomersResponse && is_array( $response->get_data()->getCustomers() ) ) {
342
343 Square\Gateway\Customer_Helper::add_customers( $response->get_data()->getCustomers() );
344
345 // if there are more customers to query, schedule a followup action to index the next batch of customers
346 if ( $response->get_data()->getCursor() ) {
347 $this->schedule_customer_index( $response->get_data()->getCursor() );
348 }
349 }
350 } catch ( \Exception $exception ) {
351
352 }
353 }
354
355
356 /**
357 * Schedules the customer index action.
358 *
359 * @since 2.0.0
360 *
361 * @param string $cursor pagination cursor
362 */
363 protected function schedule_customer_index( $cursor = '' ) {
364
365 if ( false === as_next_scheduled_action( 'wc_square_index_customers', array( $cursor ), 'square' ) ) {
366 as_schedule_single_action( time(), 'wc_square_index_customers', array( $cursor ), 'square' );
367 }
368 }
369
370
371 /** Getter methods ************************************************************************************************/
372
373
374 /**
375 * Gets the Connect button HTML.
376 *
377 * @since 2.0.0
378 *
379 * @param bool $is_sandbox whether to point the button to production or sandbox
380 * @return string
381 */
382 public function get_connect_button_html( $is_sandbox = false ) {
383
384 ob_start();
385 ?>
386 <a href="<?php echo esc_url( $this->get_connect_url( $is_sandbox ) ); ?>" class="button-primary">
387 <?php esc_html_e( 'Connect with Square', 'woocommerce-square' ); ?>
388 </a>
389 <?php
390
391 return ob_get_clean();
392 }
393
394
395 /**
396 * Gets the disconnect button HTML.
397 *
398 * @since 2.0.0
399 *
400 * @return string
401 */
402 public function get_disconnect_button_html() {
403
404 ob_start();
405 ?>
406 <a href="<?php echo esc_url( $this->get_disconnect_url() ); ?>" class='button-primary'>
407 <?php echo esc_html__( 'Disconnect from Square', 'woocommerce-square' ); ?>
408 </a>
409 <?php
410
411 return ob_get_clean();
412 }
413
414
415 /**
416 * Gets the connection URL.
417 *
418 * @since 2.0.0
419 *
420 * @param bool $is_sandbox whether to point to production or sandbox
421 * @return string
422 */
423 public function get_connect_url( $is_sandbox = false ) {
424
425 if ( $is_sandbox ) {
426 $raw_url = self::CONNECT_URL_SANDBOX;
427 } else {
428 $raw_url = self::CONNECT_URL_PRODUCTION;
429 }
430
431 /**
432 * Filters the connection URL.
433 *
434 * @since 2.0.0
435 *
436 * @param string $raw_url API URL
437 */
438 $url = (string) apply_filters( 'wc_square_api_url', $raw_url );
439
440 $action = 'wc_square_connected';
441 $redirect_url = wp_nonce_url( add_query_arg( 'action', $action, admin_url() ), $action );
442
443 $args = array(
444 'redirect' => urlencode( urlencode( $redirect_url ) ),
445 'scopes' => implode( ',', $this->get_scopes() ),
446 );
447
448 return add_query_arg( $args, $url );
449 }
450
451
452 /**
453 * Gets the disconnect URL.
454 *
455 * @since 2.0.0
456 *
457 * @return string
458 */
459 protected function get_disconnect_url() {
460
461 $action = 'wc_square_disconnect';
462 $url = add_query_arg( 'action', $action, admin_url() );
463
464 return wp_nonce_url( $url, $action );
465 }
466
467
468 /**
469 * Gets the token refresh URL.
470 *
471 * @since 2.0.0
472 *
473 * @return string
474 */
475 protected function get_refresh_url() {
476
477 return $this->get_plugin()->get_settings_handler()->is_sandbox() ? self::REFRESH_URL_SANDBOX : self::REFRESH_URL_PRODUCTION;
478 }
479
480 /**
481 * Gets the connection scopes.
482 *
483 * @since 2.0.0
484 *
485 * @return string[]
486 */
487 protected function get_scopes() {
488
489 $scopes = array(
490 'MERCHANT_PROFILE_READ',
491 'PAYMENTS_READ',
492 'PAYMENTS_WRITE',
493 'ORDERS_READ',
494 'ORDERS_WRITE',
495 'CUSTOMERS_READ',
496 'CUSTOMERS_WRITE',
497 'SETTLEMENTS_READ',
498 'ITEMS_READ',
499 'ITEMS_WRITE',
500 'INVENTORY_READ',
501 'INVENTORY_WRITE',
502 'GIFTCARDS_READ',
503 'GIFTCARDS_WRITE',
504 'PAYMENTS_WRITE',
505 'ORDERS_WRITE',
506 );
507
508 /**
509 * Hook to filter scopes.
510 *
511 * @since 2.0.0
512 */
513 return (array) apply_filters( 'wc_square_connection_scopes', $scopes );
514 }
515
516
517 /**
518 * Gets the plugin instance.
519 *
520 * @since 2.0.0
521 *
522 * @return Square\Plugin
523 */
524 public function get_plugin() {
525
526 return $this->plugin;
527 }
528
529
530 }
531