PluginProbe ʕ •ᴥ•ʔ
WooCommerce Square / 2.2.2
WooCommerce Square v2.2.2
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 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 6 years ago Background_Job.php 6 years ago Category.php 6 years ago Connection.php 6 years ago Email.php 6 years ago Order.php 6 years ago Product.php 5 years ago Products.php 6 years ago Sync.php 5 years ago
Connection.php
521 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
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
22 */
23
24 namespace WooCommerce\Square\Handlers;
25
26 defined( 'ABSPATH' ) || exit;
27
28 use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
29 use SquareConnect\Model\ListCustomersResponse;
30 use WooCommerce\Square;
31
32 /**
33 * The admin connection handler.
34 *
35 * @since 2.0.0
36 */
37 class Connection {
38
39
40 /** @var string production connect URL */
41 const CONNECT_URL_PRODUCTION = 'https://connect.woocommerce.com/login/square';
42
43 /** @var string sandbox connect URL */
44 const CONNECT_URL_SANDBOX = 'https://connect.woocommerce.com/login/squaresandbox';
45
46 /** @var string production refresh URL */
47 const REFRESH_URL_PRODUCTION = 'https://connect.woocommerce.com/renew/square';
48
49 /** @var string sandbox refresh URL */
50 const REFRESH_URL_SANDBOX = 'https://connect.woocommerce.com/renew/squaresandbox';
51
52 /** @var Square\Plugin plugin instance */
53 protected $plugin;
54
55
56 /**
57 * Constructs the class.
58 *
59 * @since 2.0.0
60 *
61 * @param Square\Plugin $plugin plugin instance
62 */
63 public function __construct( Square\Plugin $plugin ) {
64
65 $this->plugin = $plugin;
66
67 $this->add_hooks();
68 }
69
70
71 /**
72 * Adds the action and filter hooks.
73 *
74 * @since 2.0.0
75 */
76 protected function add_hooks() {
77
78 add_action( 'admin_action_wc_' . $this->get_plugin()->get_id() . '_connected', array( $this, 'handle_connected' ) );
79
80 add_action( 'admin_action_wc_' . $this->get_plugin()->get_id() . '_disconnect', array( $this, 'handle_disconnect' ) );
81
82 // refresh the connection, triggered by Action Scheduler
83 add_action( 'wc_' . $this->get_plugin()->get_id() . '_refresh_connection', array( $this, 'refresh_connection' ) );
84
85 // index customers, triggered by Action Scheduler
86 add_action( 'wc_' . $this->get_plugin()->get_id() . '_index_customers', array( $this, 'index_customers' ) );
87 }
88
89
90 /**
91 * Handles a successful connection.
92 *
93 * @internal
94 *
95 * @since 2.0.0
96 */
97 public function handle_connected() {
98
99 $nonce = isset( $_GET['_wpnonce'] ) ? wc_clean( $_GET['_wpnonce'] ) : '';
100
101 // check the user role & nonce
102 if ( ! current_user_can( 'manage_woocommerce' ) || ! wp_verify_nonce( $nonce, 'wc_' . $this->get_plugin()->get_id() . '_connected' ) ) {
103 wp_die( __( '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 _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_' . $this->get_plugin()->get_id() . '_refresh_failed' );
166
167 $nonce = isset( $_GET['_wpnonce'] ) ? wc_clean( $_GET['_wpnonce'] ) : '';
168
169 // check the user role & nonce
170 if ( ! current_user_can( 'manage_woocommerce' ) || ! wp_verify_nonce( $nonce, 'wc_' . $this->get_plugin()->get_id() . '_disconnect' ) ) {
171 wp_die( __( 'Sorry, you do not have permission to manage the Square connection.', 'woocommerce-square' ) );
172 }
173
174 // disconnect by clearing tokens, unscheduling syncs, etc...
175 $this->disconnect();
176
177 $this->get_plugin()->log( 'Manually disconnected' );
178
179 $this->get_plugin()->get_message_handler()->add_message( __( 'Disconnected successfully', 'woocommerce-square' ) );
180
181 wp_safe_redirect( $this->get_plugin()->get_settings_url() );
182 exit;
183 }
184
185
186 /**
187 * Disconnects the plugin.
188 *
189 * @since 2.0.0
190 */
191 public function disconnect() {
192
193 // don't try to refresh anymore
194 $this->unschedule_refresh();
195
196 // unschedule the interval sync
197 $this->get_plugin()->get_sync_handler()->unschedule_sync();
198
199 // fully clear the access token
200 $this->get_plugin()->get_settings_handler()->clear_access_tokens();
201 $this->get_plugin()->get_settings_handler()->clear_refresh_tokens();
202
203 // clear all background jobs so further API requests aren't attempted
204 $this->get_plugin()->get_background_job_handler()->clear_all_jobs();
205 }
206
207
208 /** Refresh methods ***********************************************************************************************/
209
210
211 /**
212 * Schedules the connection refresh.
213 *
214 * @since 2.0.0
215 */
216 public function schedule_refresh() {
217
218 if ( ! $this->get_plugin()->get_settings_handler()->is_connected() ) {
219 return;
220 }
221
222 /**
223 * Filters the frequency with which the OAuth connection should be refreshed.
224 *
225 * @since 2.0.0
226 *
227 * @param int $interval refresh interval
228 */
229 $interval = apply_filters( 'wc_' . $this->get_plugin()->get_id() . '_connection_refresh_interval', WEEK_IN_SECONDS );
230
231 // Make sure that all refresh actions are cancelled before scheduling it.
232 $this->unschedule_refresh();
233
234 as_schedule_single_action( time() + $interval, 'wc_' . $this->get_plugin()->get_id() . '_refresh_connection', array(), $this->get_plugin()->get_id() );
235 }
236
237
238 /**
239 * Refreshes the access token via the Woo proxy.
240 *
241 * @since 2.0.0
242 */
243 public function refresh_connection() {
244 if ( $this->get_plugin()->get_settings_handler()->is_sandbox() ) {
245 return;
246 }
247
248 try {
249
250 if ( $this->get_plugin()->get_settings_handler()->is_debug_enabled() ) {
251 $this->get_plugin()->log( 'Refreshing connection...' );
252 }
253
254 $refresh_token = $this->get_plugin()->get_settings_handler()->get_refresh_token();
255
256 if ( ! $refresh_token ) {
257 $this->get_plugin()->log( 'No refresh token stored, cannot refresh connection.' );
258 update_option( 'wc_' . $this->get_plugin()->get_id() . '_refresh_failed', 'yes' );
259 wc_square()->get_email_handler()->get_access_token_email()->trigger();
260 return;
261 }
262
263 $request = array(
264 'body' => array(
265 'token' => $this->get_plugin()->get_settings_handler()->get_refresh_token(),
266 ),
267 'timeout' => 45,
268 );
269
270 // make the request
271 $response = wp_remote_post( $this->get_refresh_url(), $request );
272
273 // handle HTTP errors
274 if ( is_wp_error( $response ) ) {
275 throw new Framework\SV_WC_Plugin_Exception( $response->get_error_message() );
276 }
277
278 $response = new Square\API\Responses\Connection_Refresh_Response( wp_remote_retrieve_body( $response ) );
279
280 // check for errors in the response
281 if ( $response->has_error() ) {
282 throw new Framework\SV_WC_Plugin_Exception( $response->get_error_message() );
283 }
284
285 // ensure an access token, just in case
286 if ( ! $response->get_token() ) {
287 throw new Framework\SV_WC_Plugin_Exception( 'Access token missing from the response' );
288 }
289
290 // store the new token
291 $this->get_plugin()->get_settings_handler()->update_access_token( $response->get_token() );
292
293 // In case square updates the refresh token.
294 if ( $response->get_refresh_token() ) {
295 $this->get_plugin()->get_settings_handler()->update_refresh_token( $response->get_refresh_token() );
296 $this->get_plugin()->log( 'Connection successfully refreshed.' );
297 }
298
299 // in case this option was set
300 delete_option( 'wc_' . $this->get_plugin()->get_id() . '_refresh_failed' );
301 } catch ( Framework\SV_WC_Plugin_Exception $exception ) {
302
303 $this->get_plugin()->log( 'Unable to refresh connection: ' . $exception->getMessage() );
304
305 update_option( 'wc_' . $this->get_plugin()->get_id() . '_refresh_failed', 'yes' );
306
307 wc_square()->get_email_handler()->get_access_token_email()->trigger();
308 }
309
310 $this->schedule_refresh();
311 }
312
313
314 /**
315 * Unschedules the connection refresh.
316 *
317 * @since 2.0.0
318 */
319 protected function unschedule_refresh() {
320 as_unschedule_all_actions( 'wc_' . $this->get_plugin()->get_id() . '_refresh_connection', array(), $this->get_plugin()->get_id() );
321 }
322
323
324 /** Customer index methods ****************************************************************************************/
325
326
327 /**
328 * Index existing Square customers.
329 *
330 * @since 2.0.0
331 *
332 * @param string $cursor pagination cursor
333 */
334 public function index_customers( $cursor = '' ) {
335
336 try {
337
338 $response = $this->get_plugin()->get_api()->get_customers( $cursor );
339
340 if ( $response->get_data() instanceof ListCustomersResponse && is_array( $response->get_data()->getCustomers() ) ) {
341
342 Square\Gateway\Customer_Helper::add_customers( $response->get_data()->getCustomers() );
343
344 // if there are more customers to query, schedule a followup action to index the next batch of customers
345 if ( $response->get_data()->getCursor() ) {
346 $this->schedule_customer_index( $response->get_data()->getCursor() );
347 }
348 }
349 } catch ( Framework\SV_WC_Plugin_Exception $exception ) {
350
351 }
352 }
353
354
355 /**
356 * Schedules the customer index action.
357 *
358 * @since 2.0.0
359 *
360 * @param string $cursor pagination cursor
361 */
362 protected function schedule_customer_index( $cursor = '' ) {
363
364 if ( false === as_next_scheduled_action( 'wc_' . $this->get_plugin()->get_id() . '_index_customers', array( $cursor ), $this->get_plugin()->get_id() ) ) {
365 as_schedule_single_action( time(), 'wc_' . $this->get_plugin()->get_id() . '_index_customers', array( $cursor ), $this->get_plugin()->get_id() );
366 }
367 }
368
369
370 /** Getter methods ************************************************************************************************/
371
372
373 /**
374 * Gets the Connect button HTML.
375 *
376 * @since 2.0.0
377 *
378 * @param bool $is_sandbox whether to point the button to production or sandbox
379 * @return string
380 */
381 public function get_connect_button_html( $is_sandbox = false ) {
382
383 ob_start();
384 ?>
385 <a href="<?php echo esc_url( $this->get_connect_url( $is_sandbox ) ); ?>" class="button-primary">
386 <?php esc_html_e( 'Connect with Square', 'woocommerce-square' ); ?>
387 </a>
388 <?php
389
390 return ob_get_clean();
391 }
392
393
394 /**
395 * Gets the disconnect button HTML.
396 *
397 * @since 2.0.0
398 *
399 * @return string
400 */
401 public function get_disconnect_button_html() {
402
403 ob_start();
404 ?>
405 <a href="<?php echo esc_url( $this->get_disconnect_url() ); ?>" class='button-primary'>
406 <?php echo esc_html__( 'Disconnect from Square', 'woocommerce-square' ); ?>
407 </a>
408 <?php
409
410 return ob_get_clean();
411 }
412
413
414 /**
415 * Gets the connection URL.
416 *
417 * @since 2.0.0
418 *
419 * @param bool $is_sandbox whether to point to production or sandbox
420 * @return string
421 */
422 public function get_connect_url( $is_sandbox = false ) {
423
424 if ( $is_sandbox ) {
425 $raw_url = self::CONNECT_URL_SANDBOX;
426 } else {
427 $raw_url = self::CONNECT_URL_PRODUCTION;
428 }
429
430 /**
431 * Filters the connection URL.
432 *
433 * @since 2.0.0
434 *
435 * @param string $raw_url API URL
436 */
437 $url = (string) apply_filters( 'wc_' . $this->get_plugin()->get_id() . '_api_url', $raw_url );
438
439 $action = 'wc_' . $this->get_plugin()->get_id() . '_connected';
440 $redirect_url = wp_nonce_url( add_query_arg( 'action', $action, admin_url() ), $action );
441
442 $args = array(
443 'redirect' => urlencode( urlencode( $redirect_url ) ),
444 'scopes' => implode( ',', $this->get_scopes() ),
445 );
446
447 return add_query_arg( $args, $url );
448 }
449
450
451 /**
452 * Gets the disconnect URL.
453 *
454 * @since 2.0.0
455 *
456 * @return string
457 */
458 protected function get_disconnect_url() {
459
460 $action = 'wc_' . $this->get_plugin()->get_id() . '_disconnect';
461 $url = add_query_arg( 'action', $action, admin_url() );
462
463 return wp_nonce_url( $url, $action );
464 }
465
466
467 /**
468 * Gets the token refresh URL.
469 *
470 * @since 2.0.0
471 *
472 * @return string
473 */
474 protected function get_refresh_url() {
475
476 return $this->get_plugin()->get_settings_handler()->is_sandbox() ? self::REFRESH_URL_SANDBOX : self::REFRESH_URL_PRODUCTION;
477 }
478
479 /**
480 * Gets the connection scopes.
481 *
482 * @since 2.0.0
483 *
484 * @return string[]
485 */
486 protected function get_scopes() {
487
488 $scopes = array(
489 'MERCHANT_PROFILE_READ',
490 'PAYMENTS_READ',
491 'PAYMENTS_WRITE',
492 'ORDERS_READ',
493 'ORDERS_WRITE',
494 'CUSTOMERS_READ',
495 'CUSTOMERS_WRITE',
496 'SETTLEMENTS_READ',
497 'ITEMS_READ',
498 'ITEMS_WRITE',
499 'INVENTORY_READ',
500 'INVENTORY_WRITE',
501 );
502
503 return (array) apply_filters( 'wc_' . $this->get_plugin()->get_id() . '_connection_scopes', $scopes );
504 }
505
506
507 /**
508 * Gets the plugin instance.
509 *
510 * @since 2.0.0
511 *
512 * @return Square\Plugin
513 */
514 public function get_plugin() {
515
516 return $this->plugin;
517 }
518
519
520 }
521