get_error_message() ); return $settings; } $settings['license_status'] = 'error'; $settings['license_message'] = $verify->get_error_message(); $settings['license_error_code'] = 'request_failed'; return $settings; } if ( empty( $verify['valid'] ) ) { $settings['license_status'] = 'inactive'; $settings['license_message'] = sanitize_text_field( $verify['message'] ?? __( 'License inactive.', 'agentreadyscan' ) ); $settings['license_error_code'] = sanitize_key( $verify['error_code'] ?? 'inactive' ); $settings['license_renewal_date'] = ''; $settings['license_features'] = array(); return $settings; } $settings['license_status'] = 'active'; $settings['license_message'] = sanitize_text_field( $verify['message'] ?? __( 'Pro license active.', 'agentreadyscan' ) ); $settings['license_error_code'] = ''; $settings['license_renewal_date'] = sanitize_text_field( $verify['renewal_date'] ?? '' ); $settings['license_features'] = self::sanitize_feature_list( $verify['unlocked_features'] ?? array() ); $sync = self::sync_rules( $settings['api_key'] ); if ( is_wp_error( $sync ) ) { $settings['license_last_sync_message'] = $sync->get_error_message(); return $settings; } if ( isset( $sync['valid'] ) && false === $sync['valid'] ) { $settings['license_last_sync_message'] = sanitize_text_field( $sync['message'] ?? __( 'Rules sync is unavailable for this license.', 'agentreadyscan' ) ); return $settings; } $settings['license_last_sync_at'] = current_time( 'mysql' ); $settings['license_last_sync_message'] = sanitize_text_field( sprintf( /* translators: %s: schema version */ __( 'Rules synced from AgentReadyScan API (schema %s).', 'agentreadyscan' ), $sync['schema_version'] ?? 'unknown' ) ); return $settings; } public static function is_license_check_stale( array $settings, int $max_age = WEEK_IN_SECONDS ): bool { if ( empty( $settings['api_key'] ) ) { return false; } $last_checked = self::last_checked_timestamp( $settings ); if ( ! $last_checked ) { return true; } return ( time() - $last_checked ) >= $max_age; } private static function last_checked_timestamp( array $settings ): int { $last_checked = trim( (string) ( $settings['license_checked_at'] ?? '' ) ); if ( '' === $last_checked ) { return 0; } $timestamp = strtotime( $last_checked ); return $timestamp ? (int) $timestamp : 0; } public static function clear_license_state( array $settings, string $message = '' ): array { $settings['license_status'] = 'inactive'; $settings['license_message'] = $message; $settings['license_error_code'] = ''; $settings['license_checked_at'] = current_time( 'mysql' ); $settings['license_renewal_date'] = ''; $settings['license_last_sync_at'] = ''; $settings['license_last_sync_message'] = ''; $settings['license_features'] = array(); $settings['pro_health_status'] = ''; $settings['pro_health_score'] = ''; $settings['pro_health_checked_at'] = ''; $settings['pro_health_issue_count'] = 0; $settings['pro_health_top_issues'] = array(); $settings['pro_health_recommendations'] = array(); $settings['pro_health_last_error'] = ''; return $settings; } public static function verify_license( string $api_key ) { $response = wp_remote_post( self::api_url( '/v1/licenses/verify' ), array( 'timeout' => 15, 'headers' => array( 'Content-Type' => 'application/json', ), 'body' => wp_json_encode( array( 'api_key' => $api_key, 'domain' => wp_parse_url( home_url(), PHP_URL_HOST ), 'plugin_version' => ARSCAN_VERSION, ) ), ) ); return self::decode_json_response( $response ); } public static function sync_rules( string $api_key ) { $response = wp_remote_get( self::api_url( '/v1/sync/rules' ), array( 'timeout' => 15, 'headers' => array( 'Authorization' => 'Bearer ' . $api_key, ), ) ); return self::decode_json_response( $response ); } public static function create_billing_portal_session( string $api_key ) { $response = wp_remote_post( self::api_url( '/api/create-billing-portal-session' ), array( 'timeout' => 15, 'headers' => array( 'Content-Type' => 'application/json', ), 'body' => wp_json_encode( array( 'api_key' => $api_key, ) ), ) ); return self::decode_json_response( $response ); } public static function sync_pro_snapshot( string $api_key, array $snapshot, string $settings_hash = '' ) { $response = wp_remote_post( self::api_url( '/v1/pro/snapshot' ), array( 'timeout' => 20, 'headers' => array( 'Content-Type' => 'application/json', 'Authorization' => 'Bearer ' . $api_key, ), 'body' => wp_json_encode( array( 'plugin_version' => ARSCAN_VERSION, 'domain' => wp_parse_url( home_url(), PHP_URL_HOST ), 'settings_hash' => $settings_hash, 'snapshot' => $snapshot, ) ), ) ); return self::decode_json_response( $response ); } public static function fetch_latest_pro_audit( string $api_key ) { $domain = rawurlencode( (string) wp_parse_url( home_url(), PHP_URL_HOST ) ); $response = wp_remote_get( self::api_url( '/v1/pro/audit/latest?domain=' . $domain ), array( 'timeout' => 15, 'headers' => array( 'Authorization' => 'Bearer ' . $api_key, ), ) ); return self::decode_json_response( $response ); } private static function decode_json_response( $response ) { if ( is_wp_error( $response ) ) { return $response; } $status = (int) wp_remote_retrieve_response_code( $response ); $body = json_decode( wp_remote_retrieve_body( $response ), true ); if ( ! is_array( $body ) ) { return new WP_Error( 'arscan_invalid_response', __( 'AgentReadyScan returned an unreadable response.', 'agentreadyscan' ) ); } if ( $status >= 400 ) { $error_message = sanitize_text_field( $body['message'] ?? $body['error'] ?? __( 'License server returned an error.', 'agentreadyscan' ) ); $error_code = sanitize_key( $body['error_code'] ?? ( 'inactive' === ( $body['status'] ?? '' ) ? 'inactive' : 'server_error' ) ); return new WP_Error( 'arscan_license_' . $error_code, $error_message ); } return $body; } private static function api_url( string $path ): string { $base = untrailingslashit( apply_filters( 'arscan_api_base', ARSCAN_API_BASE ) ); return $base . $path; } private static function sanitize_feature_list( $features ): array { if ( ! is_array( $features ) ) { return array(); } $clean = array(); foreach ( $features as $feature ) { $feature = sanitize_key( $feature ); if ( '' !== $feature ) { $clean[] = $feature; } } return array_values( array_unique( $clean ) ); } }