clock = is_callable($clock) ? $clock : 'time'; $this->uuid = is_callable($uuid) ? $uuid : 'wp_generate_uuid4'; } public static function canonical($timestamp, $request_id) { return implode("\n", array('AKKEDLI-WC-V1', 'GET', self::PATH, $timestamp, $request_id, '', hash('sha256', ''))); } public function check(array $state) { if (!Akkedli_V2_Binding_State::valid($state) || 'v2_pending' !== $state['mode']) { return array('kind' => 'manual'); } try { $timestamp = (string) call_user_func($this->clock); $request_id = strtolower((string) call_user_func($this->uuid)); $secret = Akkedli_V2_Crypto::base64url_decode($state['secret']); } catch (Throwable $error) { unset($error); return array('kind' => 'manual'); } if (32 !== strlen($secret) || !preg_match('/^[1-9]\d{9,10}$/D', $timestamp) || !preg_match('/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/D', $request_id)) { return array('kind' => 'manual'); } $signature = 'v1=' . Akkedli_V2_Crypto::base64url_encode(hash_hmac( 'sha256', self::canonical($timestamp, $request_id), $secret, true )); $args = array('timeout' => 10, 'redirection' => 0, 'sslverify' => true, 'reject_unsafe_urls' => true, 'limit_response_size' => self::LIMIT, 'cookies' => array(), 'headers' => array( 'X-Akkedli-Connector-Id' => $state['credential_id'], 'X-Akkedli-Timestamp' => $timestamp, 'X-Akkedli-Request-Id' => $request_id, 'X-Akkedli-Signature' => $signature, )); unset($secret, $signature, $timestamp, $request_id); try { $response = wp_safe_remote_get(self::URL, $args); } catch (Throwable $error) { unset($error); return array('kind' => 'retry'); } if (is_wp_error($response)) return array('kind' => 'retry'); $code = wp_remote_retrieve_response_code($response); if (401 === $code || 403 === $code) return array('kind' => 'terminal'); if (429 === $code) return self::retry($response); if (408 === $code || ($code >= 500 && $code <= 599)) return array('kind' => 'retry'); if (404 === $code) { $failure = Akkedli_V2_Strict_JSON::failure_code(wp_remote_retrieve_body($response)); return 'WOOCOMMERCE_CONNECTOR_DISABLED' === $failure ? array('kind' => 'paused') : array('kind' => 'manual'); } if (200 !== $code) return array('kind' => 'manual'); $type = wp_remote_retrieve_header($response, 'content-type'); $body = wp_remote_retrieve_body($response); if (!is_string($type) || !preg_match('/^application\/json(?:;\s*charset=utf-8)?$/Di', $type) || !is_string($body) || strlen($body) >= self::LIMIT) return array('kind' => 'manual'); $value = Akkedli_V2_Strict_JSON::decode($body, array('success', 'data'), array('binding_status')); if (!is_array($value) || true !== $value['success'] || !is_string($value['data']['binding_status']) || !in_array($value['data']['binding_status'], array('pending', 'verified', 'failed'), true)) { return array('kind' => 'manual'); } return array('kind' => 'status', 'status' => $value['data']['binding_status']); } private static function retry($response) { $header = wp_remote_retrieve_header($response, 'retry-after'); return is_string($header) && preg_match('/^[1-9]\d{0,2}$/D', $header) ? array('kind' => 'retry', 'retry_after' => min(300, (int) $header)) : array('kind' => 'retry'); } }