/** * Utilize WC logger class * * @since 1.0.0 * @version 1.0.0 */ public static function log($message, $start_time = null, $end_time = null) { if (empty(self::$logger)) { self::$logger = new WC_Logger(); } $settings = get_option('woocommerce_squareconnect_settings', ''); if (!empty($settings['logging']) && 'yes' !== $settings['logging']) { return; } if (!is_null($start_time)) { $formatted_start_time = date_i18n(get_option('date_format') . ' g:ia', $start_time); $end_time = is_null($end_time) ? current_time('timestamp') : $end_time; $formatted_end_time = date_i18n(get_option('date_format') . ' g:ia', $end_time); $elapsed_time = round(abs($end_time - $start_time) / 60, 2); $log_entry = '====Start Log ' . $formatted_start_time . '====' . "\n" . $message . "\n"; $log_entry .= '====End Log ' . $formatted_end_time . ' (' . $elapsed_time . ')====' . "\n\n"; } else { $log_entry = '====Start Log====' . "\n" . $message . "\n" . '====End Log====' . "\n\n"; } self::$logger->add(self::WC_LOG_FILENAME, $log_entry); }
/** * Update a Square Item Image for a WC Product * * @param WC_Product $wc_product * @param string $square_item_id * @return bool Success. */ public function update_product_image(WC_Product $wc_product, $square_item_id) { $image_id = get_post_thumbnail_id($wc_product->id); if (empty($image_id)) { WC_Square_Sync_Logger::log(sprintf('[WC -> Square] Update Product Image: No thumbnail ID for WC Product %d.', $wc_product->id)); return true; } $mime_type = get_post_field('post_mime_type', $image_id, 'raw'); $image_path = get_attached_file($image_id); $result = $this->connect->update_square_product_image($square_item_id, $mime_type, $image_path); if ($result && isset($result->id)) { WC_Square_Utils::update_wc_product_image_square_id($wc_product->id, $result->id); return true; } else { WC_Square_Sync_Logger::log(sprintf('[WC -> Square] Error updating Product Image for WC Product %d.', $wc_product->id)); return false; } }
/** * Helper method to make HTTP requests to the Square API, with retries. * * @param string $debug_label Description of the request, for logging. * @param string $request_url URL to request. * @param string $method HTTP method to use. Defaults to 'GET'. * @param mixed $body Optional. Request payload - will be JSON encoded if non-scalar. * * @return bool|object|WP_Error */ private function http_request($debug_label, $request_url, $method = 'GET', $body = null) { $request_args = $this->get_request_args(); if (!is_null($body)) { if (!empty($request_args['headers']['Content-Type']) && 'application/json' === $request_args['headers']['Content-Type']) { $request_args['body'] = json_encode($body); } else { $request_args['body'] = $body; } } $request_args['method'] = $method; // Make actual request in a retry loop $try_count = 1; $max_retries = $this->request_retries(); while (true) { $start_time = current_time('timestamp'); $response = wp_remote_request(untrailingslashit($request_url), $request_args); $end_time = current_time('timestamp'); WC_Square_Sync_Logger::log(sprintf('%s', $debug_label), $start_time, $end_time); $decoded_response = json_decode(wp_remote_retrieve_body($response)); // check for bad request and log it if (is_object($decoded_response) && !empty($decoded_response->type) && preg_match('/bad_request/', $decoded_response->type)) { WC_Square_Sync_Logger::log(sprintf('%s - %s', $decoded_response->type, $decoded_response->message), $start_time, $end_time); } // handle expired tokens if (is_object($decoded_response) && !empty($decoded_response->type) && 'oauth.expired' === $decoded_response->type) { $oauth_connect_url = 'https://connect.woocommerce.com/renew/square'; if (WC_SQUARE_ENABLE_STAGING) { $oauth_connect_url = 'https://connect.woocommerce.com/renew/squaresandbox'; } $args = array('body' => array('token' => $this->access_token)); $start_time = current_time('timestamp'); $oauth_response = wp_remote_request($oauth_connect_url, $args); $end_time = current_time('timestamp'); $decoded_oauth_response = json_decode(wp_remote_retrieve_body($oauth_response)); if (is_wp_error($oauth_response)) { WC_Square_Sync_Logger::log(sprintf('Renewing expired token error - %s', $oauth_response->get_error_message()), $start_time, $end_time); return false; } elseif ($decoded_oauth_response->error) { WC_Square_Sync_Logger::log(sprintf('Renewing expired token error - %s', $decoded_oauth_response->type), $start_time, $end_time); return false; } else { update_option('woocommerce_square_merchant_access_token', sanitize_text_field(urldecode($decoded_oauth_response->access_token))); // let's set the token instance again so settings option is refreshed $this->set_access_token(sanitize_text_field(urldecode($decoded_oauth_response->access_token))); WC_Square_Sync_Logger::log(sprintf('Retrying with new refreshed token'), $start_time, $end_time); // start at the beginning again continue; } } // handle revoked tokens if (is_object($decoded_response) && !empty($decoded_response->type) && 'oauth.revoked' === $decoded_response->type) { WC_Square_Sync_Logger::log(sprintf('Token is revoked!'), $start_time, $end_time); return false; } if (is_wp_error($response)) { WC_Square_Sync_Logger::log(sprintf('(%s) Try #%d - %s', $debug_label, $try_count, $response->get_error_message()), $start_time, $end_time); } else { return $response; } $try_count++; if ($try_count > $max_retries) { break; } sleep(1); } return false; }
/** * Process Square to WC ajax * * @access public * @since 1.0.0 * @version 1.0.0 * @return bool */ public function square_to_wc_ajax() { $nonce = $_POST['ajaxSyncNonce']; // bail if nonce don't check out if (!wp_verify_nonce($nonce, '_wc_square_sync_nonce')) { wp_die(__('Cheatin’ huh?', 'woocommerce-square')); } /** * Fires if a valid bulk Square to WC sync is being processed. * * @since 1.0.0 */ do_action('woocommerce_square_bulk_syncing_square_to_wc'); $settings = get_option('woocommerce_squareconnect_settings'); $emails = !empty($settings['sync_email']) ? $settings['sync_email'] : ''; $sync_products = 'yes' === $settings['sync_products']; $sync_categories = 'yes' === $settings['sync_categories']; $sync_inventory = 'yes' === $settings['sync_inventory']; $sync_images = 'yes' === $settings['sync_images']; $message = ''; if (!$sync_products) { wp_send_json(array('process' => 'done', 'percentage' => 100, 'type' => 'square-to-wc', 'message' => __('Product Sync is disabled. Sync aborted.', 'woocommerce-square'))); } // if a WC to Square process still needs to be completed reset the caches // as the two processes ( WC -> Square and Square -> WC ) use the same cache if ('wc_to_square' === get_transient('sq_wc_sync_current_process')) { $this->delete_all_caches(); } // set Square->WC as the current active process set_transient('sq_wc_sync_current_process', 'square_to_wc', apply_filters('woocommerce_square_syncing_current_process_cache_time', DAY_IN_SECONDS)); // index for the current item in the process $process = $this->get_process_index(); // ensure this manual update gets the freshest item counts delete_transient('wc_square_inventory'); // only sync categories on the first pass if (0 === $process && $sync_categories) { $this->square_to_wc->sync_categories(); } // products // get all product ids $square_item_ids = $this->get_processing_ids(); // run this only on first process if ($process === 0) { $square_items = $this->connect->get_square_products(); $square_item_ids = !empty($square_items) ? array_unique(wp_list_pluck($square_items, 'id')) : array(); // cache it $cache_age = apply_filters('woocommerce_square_syncing_square_ids_cache', DAY_IN_SECONDS); set_transient('wc_square_processing_total_count', count($square_item_ids), $cache_age); set_transient('wc_square_processing_ids', $square_item_ids, $cache_age); } if ($square_item_ids && $sync_products) { $square_item_id = array_pop($square_item_ids); $square_item = $this->connect->get_square_product($square_item_id); if ($square_item) { $this->square_to_wc->sync_product($square_item, $sync_categories, $sync_inventory, $sync_images); } else { WC_Square_Sync_Logger::log(sprintf('[Square -> WC] Bulk Sync: Error retrieving Square Item with ID %s.', $square_item_id)); } $process++; $percentage = $this->get_process_percentage($process); $this->delete_processed_id($square_item_id); $remaining_ids = $this->get_processing_ids(); // run this only on last process if (empty($remaining_ids)) { $process = 'done'; $percentage = 100; // send sync email $this->send_sync_email($emails, __('Sync Completed', 'woocommerce-square')); // reset the processed ids $this->delete_all_caches(); $message = __('Sync completed', 'woocommerce-square'); } wp_send_json(array('process' => $process, 'percentage' => $percentage, 'type' => 'square-to-wc', 'message' => $message)); } }
/** * Retrieve the WC Category ID that corresponds to a given Square Category ID. * * @param string $square_cat_id * @return bool|int WC Category ID on successful match, boolean false otherwise. */ public static function get_wc_category_id_for_square_category_id($square_cat_id) { $categories = get_terms('product_cat', array('parent' => 0, 'hide_empty' => false, 'fields' => 'ids')); if (is_wp_error($categories)) { WC_Square_Sync_Logger::log(sprintf('%s::%s - Taxonomy "product_cat" not found. Make sure WooCommerce is enabled.', __CLASS__, __FUNCTION__)); return false; } foreach ($categories as $wc_category) { $wc_square_cat_id = self::get_wc_term_square_id($wc_category); if ($wc_square_cat_id && $square_cat_id === $wc_square_cat_id) { return $wc_category; } } return false; }
/** * Update an existing WC Product using data from Square. * * @param WC_Product $wc_product * @param object $square_item * @param bool $include_category * @param bool $include_inventory * @param bool $include_image * * @return bool|WC_Product Updated WC_Product on success, boolean false on failure. */ public function update_product(WC_Product $wc_product, $square_item, $include_category = false, $include_inventory = false, $include_image = false) { $product_update = WC_Square_Utils::format_square_item_for_wc_api_update($square_item, $wc_product, $include_category, $include_inventory, $include_image); $result = $this->connect->wc->edit_product($wc_product->id, array('product' => $product_update)); if (is_wp_error($result)) { WC_Square_Sync_Logger::log(sprintf('[Square -> WC] Error updating WC Product %d for Square ID %s: %s', $square_item->id, $result->get_error_message())); } elseif (isset($result['product']['id'])) { return wc_get_product($result['product']['id']); } return false; }