15, 'blocking' => true, 'auth' => true, ) ); // Time zone and locale are only used to show local prices before a billing country is saved. $headers = array( 'Accept' => 'application/json', 'X-Clientager-Site' => self::site_id(), 'X-Clientager-Timezone' => wp_timezone_string(), 'X-Clientager-Locale' => get_locale(), ); if ( $opts['auth'] ) { $token = self::token(); if ( ! $token ) { return self::fail( 0, __( 'Connect your Clientager account first.', 'clientager' ), 'not_connected' ); } $headers['Authorization'] = 'Bearer ' . $token; } $args = array( 'method' => $method, 'timeout' => $opts['timeout'], 'blocking' => $opts['blocking'], 'headers' => $headers, 'user-agent' => 'Clientager-WordPress/' . CLIENTAGER_VERSION . '; ' . home_url( '/' ), ); if ( null !== $body ) { $args['headers']['Content-Type'] = 'application/json'; $args['body'] = wp_json_encode( $body ); } $res = wp_remote_request( self::base() . $path, $args ); if ( ! $opts['blocking'] ) { return array( 'ok' => true, 'status' => 0, 'data' => array(), 'error' => '', 'code' => '', ); } if ( is_wp_error( $res ) ) { return self::fail( 0, __( 'Could not reach Clientager. Please try again in a moment.', 'clientager' ), 'network' ); } $status = (int) wp_remote_retrieve_response_code( $res ); $data = json_decode( wp_remote_retrieve_body( $res ), true ); $data = is_array( $data ) ? $data : array(); if ( $status >= 200 && $status < 300 ) { return array( 'ok' => true, 'status' => $status, 'data' => $data, 'error' => '', 'code' => '', ); } $code = isset( $data['code'] ) ? sanitize_key( $data['code'] ) : ''; if ( 401 === $status && 'bad_token' === $code ) { self::forget(); } $msg = isset( $data['error'] ) ? sanitize_text_field( $data['error'] ) : __( 'Something went wrong. Please try again.', 'clientager' ); $out = self::fail( $status, $msg, $code ); $out['data'] = $data; return $out; } /** * Error result. * * @param int $status HTTP status. * @param string $error Message. * @param string $code Machine code. * @return array */ private static function fail( $status, $error, $code ) { return array( 'ok' => false, 'status' => $status, 'data' => array(), 'error' => $error, 'code' => $code, ); } /** * Account status. Fresh from the server, or cached for 10 minutes when $cached is true. * When the server cannot be reached, the last good answer is used for up to 24 hours. * * @param bool $cached Use the 10-minute cache. * @return array|null Status, or null when not connected. */ public static function status( $cached = false ) { if ( ! self::token() ) { return null; } if ( $cached ) { $hit = get_transient( self::STATUS_CACHE ); if ( is_array( $hit ) ) { return $hit; } } $res = self::request( 'GET', '/status', null, array( 'timeout' => $cached ? 3 : 10 ) ); if ( $res['ok'] ) { set_transient( self::STATUS_CACHE, $res['data'], 10 * MINUTE_IN_SECONDS ); update_option( self::OPT_LAST_STATUS, array( 'at' => time(), 'status' => $res['data'], ), false ); return $res['data']; } if ( in_array( $res['code'], array( 'bad_token', 'blocked', 'not_connected' ), true ) ) { self::clear_status(); return 'blocked' === $res['code'] ? array( 'blocked' => true ) : null; } $last = get_option( self::OPT_LAST_STATUS ); if ( is_array( $last ) && isset( $last['at'], $last['status'] ) && time() - (int) $last['at'] < DAY_IN_SECONDS ) { // Retry in a minute rather than on every page view. set_transient( self::STATUS_CACHE, $last['status'], MINUTE_IN_SECONDS ); return $last['status']; } return $cached ? null : array( 'unreachable' => true ); } /** * Drops the cached status so the next read asks the server. */ public static function clear_status() { delete_transient( self::STATUS_CACHE ); } }