<?php
/**
 * The WC Software License Client Library
 *
 * This class defines all code necessary to check for a valid license and available updates stored on your WooCommerce Software License Server
 *
 *
 * To integrate this into your software product include the following code in your MAIN plugin file, do not attempt
 * to add this code in any other file but your main plugin file.
 *
 *        // Required Parameters
 *
 * @param string  required $license_server_url - The base url to your woocommerce shop
 * @param string  required $version - the software version currently running
 * @param string  required $text_domain - the text domain of the plugin
 * @param string  required $plugin_file - path to the plugin file or directory, relative to the plugins directory
 * @param string  required $plugin_nice_name - A nice name for the plugin for use in messages
 *
 *         // Optional Parameters
 * @param string optional $slug - the plugin slug if your class file name is different to the specified slug on the WooCommerce Product
 * @param integer optional $update_interval - time in hours between update checks
 * @param bool optional $debug - enable debugging in the client library.
 *
 *  require_once plugin_dir_path( __FILE__ ) . 'path/to/wc-software-license-client/class-wc-software-license-client.php';
 *
 *    function wcslc_instance(){
 *        return WC_Software_License_Client::get_instance( 'http://yourshopurl.here.com', 1.0.0, 'your-text-domain', __FILE__, 'My Cool Plugin' );
 *    } // wcslc_instance()
 *
 *    wcslc_instance();
 *
 * @version        1.0.3
 * @since          1.0.0
 * @package        WC_Software_License_Client
 * @author         Jamie Madden <github.com/digitalchild>
 * @phpcs:disable
 */

if ( ! defined( 'ABSPATH' ) ) {
    exit; // Exit if accessed directly.
}

if ( ! class_exists( 'WC_Software_License_Client' ) ) :

    class WC_Software_License_Client {

        /**
         * Instance of this class.
         *
         * @var object
         */
        private static $instances = array();

        /**
         * Version - current plugin version
         *
         * @since 1.0.0
         */
        public $version;

        /**
         * License URL - The base URL for your woocommerce install
         *
         * @since 1.0.0
         */
        public $license_server_url;

        /**
         * Renewal URL - the url to renew the product
         *
         * @since 1.0.3
         */
        public $renewal_url;

        /**
         * Slug - the plugin slug to check for updates with the server
         *
         * @since 1.0.0
         */
        public $slug;

        /**
         * Plugin text domain
         *
         * @since 1.0.0
         */
        public $text_domain;

        /**
         * Path to the plugin file or directory, relative to the plugins directory
         *
         * @since 1.0.0
         */
        public $plugin_file;

        /**
         * Path to the plugin file or directory, relative to the plugins directory
         *
         * @since 1.0.0
         */
        public $plugin_nice_name;

        /**
         * Update interval - what period in hours to check for updates defaults to 12;
         *
         * @since 1.0.0
         */
        public $update_interval;

        /**
         * Option name - wp option name for license and update information stored as $slug_wc_software_license
         *
         * @since 1.0.0
         */
        public $option_name;

        /**
         * The license server host
         */
        private $license_server_host;

        /**
         * The plugin license key
         */
        private $license_key;

        /**
         * The domain the plugin is running on
         */
        private $domain;

        /**
         * The plugin license key
         *
         * @since  1.0.0
         * @access private
         */
        private $admin_notice;

        /**
         * Whether debug is enabled or not.
         *
         * @var bool
         */
        private $debug;

        /**
         * License manager URL
         *
         * @var string
         */
        private $license_manager_url;

        /**
         * License details
         *
         * @var array
         */
        private $license_details;

        /**
         * Don't allow cloning
         *
         * @since 1.0.0
         */
        public function __clone() {
        }

        /**
         * Don't allow unserializing instances of this class
         *
         * @since 1.0.0
         */
        public function __wakeup() {
        }

        /**
         * Return an instance of this class.
         *
         * @since 1.0.0
         *
         * @param string  $license_server_url - The base url to your woocommerce shop
         * @param string  $version            - the software version currently running
         * @param string  $text_domain        - the text domain of the plugin
         * @param string  $plugin_file        - path to the plugin file or directory, relative to the plugins directory
         * @param string  $plugin_nice_name   - A nice name for the plugin for use in messages
         * @param integer $update_interval    - time in hours between update checks
         * @param bool    $debug              - pass if the plugin is in debug mode
         *
         * @return object A single instance of this class.
         */
        public static function get_instance( $license_server_url, $renewal_url, $version, $text_domain, $plugin_file, $plugin_nice_name, $slug = '', $update_interval = 12, $debug = false ) {

            if ( ! array_key_exists( $text_domain, self::$instances ) ) {
                self::$instances[ $text_domain ] = new self( $license_server_url, $renewal_url, $version, $text_domain, $plugin_file, $plugin_nice_name, $slug, $update_interval, $debug );
            }

            return self::$instances;
        } // get_instance()

        /**
         * Initialize the class actions
         *
         * @since 1.0.0
         *
         * @param string  $license_server_url - The base url to your woocommerce shop
         * @param string  $version            - the software version currently running
         * @param string  $text_domain        - the text domain of the plugin
         * @param string  $plugin_file        - path to the plugin file or directory, relative to the plugins directory
         * @param string  $plugin_nice_name   - A nice name for the plugin for use in messages
         * @param integer $update_interval    - time in hours between update checks
         */
        private function __construct( $license_server_url, $renewal_url, $version, $text_domain, $plugin_file, $plugin_nice_name, $slug, $update_interval, $debug ) {

            $this->plugin_nice_name    = $plugin_nice_name;
            $this->license_server_url  = $license_server_url;
            $this->renewal_url         = $renewal_url;
            $this->version             = $version;
            $this->text_domain         = $text_domain;
            $this->plugin_file         = plugin_basename( $plugin_file );
            $this->update_interval     = $update_interval;
            $this->debug               = defined( 'WP_DEBUG' ) && WP_DEBUG ? true : $debug;
            $this->slug                = empty( $slug ) ? basename( $this->plugin_file, '.php' ) : $slug;
            $this->option_name         = $this->slug . '_license_manager';
            $this->domain              = str_ireplace( array( 'http://', 'https://' ), '', home_url() );
            $this->license_details     = get_option( $this->option_name );
            $this->license_manager_url = esc_url( admin_url( 'options-general.php?page=' . $this->slug . '_license_manager' ) );

            // Get the license server host
            $this->license_server_host = @parse_url( $this->license_server_url, PHP_URL_HOST );

            // Initilize wp-admin interfaces
            add_action( 'admin_init', array( $this, 'check_install' ) );
            add_action( 'admin_menu', array( $this, 'add_license_menu' ) );
            add_action( 'admin_init', array( $this, 'add_license_settings' ) );

            // Internal methods
            add_filter( 'http_request_host_is_external', array( $this, 'fix_update_host' ), 10, 2 );

            // Only allow updates if they have a valid license key
            if ( is_array( $this->license_details ) && array_key_exists( 'license_status', $this->license_details ) && 'active' === $this->license_details['license_status'] ) {

                add_filter( 'pre_set_site_transient_update_plugins', array( $this, 'update_check' ) );
                add_filter( 'plugins_api', array( $this, 'add_plugin_info' ), 10, 3 );
                add_filter( 'plugin_row_meta', array( $this, 'check_for_update_link' ), 10, 2 );

                add_action( 'admin_init', array( $this, 'process_manual_update_check' ) );
                add_action( 'all_admin_notices', array( $this, 'output_manual_update_check_result' ) );

            }
        } // __construct()

        /**
         * Check the installation and configure any defaults that are required
         *
         * @since 1.0.0
         * @todo  move this to a plugin activation hook
         */
        public function check_install() {

            // Set defaults
            if ( empty( $this->license_details ) ) {
                $default_license_options = array(
                    'license_status'     => 'inactive',
                    'license_key'        => '',
                    'license_expires'    => '',
                    'deactivate_license' => '',
                    'current_version'    => $this->version,
                );

                update_option( $this->option_name, $default_license_options );

            }

            if ( $this->license_details == '' || $this->license_details['license_status'] == 'inactive' || $this->license_details['license_status'] == 'deactivated' ) {
                add_action( 'admin_notices', array( $this, 'license_inactive' ) );
            } elseif ( $this->license_details['license_status'] == 'expired' ) {
                add_action( 'admin_notices', array( $this, 'license_expired' ) );
            }
        } // check_install()

        /**
         * Display a license inactive notice
         */
        public function license_inactive() {

            $screen = get_current_screen();
            if ( ! in_array( $screen->id, wcv_get_screen_ids() ) && 'plugins' !== $screen->id ) {
                return;
            }
            if ( ! current_user_can( 'manage_options' ) ) {
                return;
            }

            echo '<div class="error notice is-dismissible"><p>' .
                sprintf( __( 'The %1$s license key has not been activated, so you will be unable to get automatic updates or support! %2$sClick here%3$s to activate your support and updates license key.', 'wcvendors-pro' ), $this->plugin_nice_name, '<a href="' . $this->license_manager_url . '">', '</a>' ) .
                '</p></div>';
        } // license_inactive()

        /**
         *    Display the license has expired code
         */
        public function license_expired() {

            if ( ! current_user_can( 'manage_options' ) ) {
                return;
            }

            echo '<div class="error notice is-dismissible"><p>' .
                sprintf( __( '<strong>The %1$s license key has expired, you will be unable to get automatic updates or support! </strong> %2$sClick here%3$s to purchase your new support and updates license key.', 'wcvendors-pro' ), $this->plugin_nice_name, '<a href="' . $this->renewal_url . '">', '</a>' ) .
                '</p></div>';
        }

        /**
         * Check for updates with the license server
         *
         * @since 1.0.0
         *
         * @param object transient object from the update api
         *
         * @return object transient object possibly modified
         */
        public function update_check( $transient ) {

            if ( empty( $transient->checked ) ) {
                return $transient;
            }

            $server_response = $this->server_request( 'check_update' );

            if ( $this->check_license( $server_response ) ) {

                if ( isset( $server_response ) && is_object( $server_response->software_details ) ) {

                    $plugin_update_info = $server_response->software_details;

                    if ( isset( $plugin_update_info->new_version ) ) {
                        if ( version_compare( $plugin_update_info->new_version, $this->version, '>' ) ) {
                            // Required to cast as array due to how object is returned from api
                            $plugin_update_info->sections              = (array) $plugin_update_info->sections;
                            $transient->response[ $this->plugin_file ] = $plugin_update_info;
                        }
                    }
                }
            }

            return $transient;
        } // update_check()

        /**
         * Add the plugin information to the WordPress Update API
         *
         * @since 1.0.0
         *
         * @param bool|object The result object. Default false.
         * @param string The type of information being requested from the Plugin Install API.
         * @param object Plugin API arguments.
         *
         * @return object
         */
        public function add_plugin_info( $result, $action = null, $args = null ) {

            // Is this about our plugin?
            if ( isset( $args->slug ) ) {

                if ( $args->slug != $this->slug ) {
                    return $result;
                }
            } else {
                return $result;
            }

            $server_response    = $this->server_request();
            $plugin_update_info = $server_response->software_details;
            // Required to cast as array due to how object is returned from api
            $plugin_update_info->sections = (array) $plugin_update_info->sections;

            if ( isset( $plugin_update_info ) && is_object( $plugin_update_info ) && $plugin_update_info !== false ) {
                return $plugin_update_info;
            }

            return $result;
        } // add_plugin_info()

        /**
         * Send a request to the server
         *
         * @param $action string activate|deactivate|check_update
         */
        public function server_request( $action = 'check_update' ) {

            $request_info['slug']        = $this->slug;
            $request_info['license_key'] = $this->license_details['license_key'];
            $request_info['domain']      = $this->domain;
            $request_info['version']     = $this->version;

            // Allow filtering the request info for plugins
            $request_info = apply_filters( 'wcsl_request_info_' . $this->slug, $request_info );

            // Build the server url api end point fix url build to support the WordPress API
            $server_request_url = esc_url_raw( $this->license_server_url . 'wp-json/wpsls/v1/' . $action . '?' . http_build_query( $request_info ) );

            // Options to parse the wp_safe_remote_get() call
            $request_options = array( 'timeout' => 20 );

            // Allow filtering the request options
            $request_options = apply_filters( 'wcsl_request_options_' . $this->slug, $request_options );

            // Query the license server
            $response = wp_safe_remote_get( $server_request_url, $request_options );

            // Validate that the response is valid not what the response is
            $result = $this->validate_response( $response );

            // Check if there is an error and display it if there is one, otherwise process the response.
            if ( ! is_wp_error( $result ) ) {

                $response_body = json_decode( wp_remote_retrieve_body( $response ) );

                // Check the status of the response
                $continue = $this->check_response_status( $response_body );

                if ( $continue ) {
                    return $response_body;
                }
            } else {

                // Display the error message in admin
                add_settings_error(
                    $this->option_name,
                    esc_attr( 'settings_updated' ),
                    $result->get_error_message(),
                    'error'
                );

                // Return null to halt the execution
                return null;

            }
        } // server_request()

        /**
         * Validate the license server response to ensure its valid response not what the response is
         *
         * @since  1.0.0
         * @access public
         *
         * @param WP_Error | Array The response or WP_Error
         */
        public function validate_response( $response ) {

            if ( ! empty( $response ) ) {

                // Can't talk to the server at all, output the error
                if ( is_wp_error( $response ) ) {
                    return new WP_Error( $response->get_error_code(), sprintf( __( 'HTTP Error: %s', $this->text_domain ), $response->get_error_message() ) );
                }

                // There was a problem with the initial request
                if ( ! isset( $response['response']['code'] ) ) {
                    return new WP_Error( 'wcsl_no_response_code', __( 'wp_safe_remote_get() returned an unexpected result.', $this->text_domain ) );
                }

                // There is a validation error on the server side, output the problem
                if ( $response['response']['code'] == 400 ) {

                    $body = json_decode( $response['body'] );

                    foreach ( $body->data->params as $param => $message ) {
                        return new WP_Error( 'wcsl_validation_failed', sprintf( __( 'There was a problem with your license: %s', $this->text_domain ), $message ) );
                    }
                }

                // The server is broken
                if ( $response['response']['code'] == 500 ) {
                    return new WP_Error( 'wcsl_internal_server_error', sprintf( __( 'There was a problem with the license server: HTTP response code is : %s', $this->text_domain ), $response['response']['code'] ) );
                }

                if ( $response['response']['code'] !== 200 ) {
                    return new WP_Error( 'wcsl_unexpected_response_code', sprintf( __( 'HTTP response code is : % s, expecting ( 200 )', $this->text_domain ), $response['response']['code'] ) );
                }

                if ( empty( $response['body'] ) ) {
                    return new WP_Error( 'wcsl_no_response', __( 'The server returned no response.', $this->text_domain ) );
                }

                return true;

            }
        } // validate_response()

        /**
         * Validate the license server response to ensure its valid response not what the response is
         *
         * @since  1.0.0
         * @access public
         *
         * @param object $response_body
         */
        public function check_response_status( $response_body ) {

            if ( is_object( $response_body ) && ! empty( $response_body ) ) {

                $license_status_types = $this->license_status_types();
                $status               = $response_body->status;

                return ( array_key_exists( $status, $license_status_types ) ) ? true : false;
            }

            return false;
        }  // check_response_status()

        /**
         * Validate the license is active and if not, set the status and return false
         *
         * @since  1.0.0
         * @access public
         *
         * @param object $response_body
         */
        public function check_license( $response_body ) {

            $status = $response_body->status;

            if ( 'active' === $status || 'expiring' === $status ) {
                return true;
            }

            $this->set_license_status( $status );
            $this->set_license_expires( $response_body->expires );
            $this->save();

            return false;
        } // check_license()

        /**
         * Add a check for update link on the plugins page. You can change the link with the supplied filter.
         * returning an empty string will disable this link
         *
         * @since  1.0.0
         * @access public
         *
         * @param array  $links The array having default links for the plugin.
         * @param string $file  The name of the plugin file.
         */
        public function check_for_update_link( $links, $file ) {

            // Only modify the plugin meta for our plugin
            if ( $file == $this->plugin_file && current_user_can( 'update_plugins' ) ) {

                $update_link_url = wp_nonce_url(
                    add_query_arg(
                        array(
                            'wcsl_check_for_update' => 1,
                            'wcsl_slug'             => $this->slug,
                        ),
                        self_admin_url( 'plugins.php' )
                    ),
                    'wcsl_check_for_update'
                );

                $update_link_text = apply_filters( 'wcsl_update_link_text_' . $this->slug, __( 'Check for updates', $this->text_domain ) );

                if ( ! empty( $update_link_text ) ) {
                    $links[] = sprintf( '<a href="%s">%s</a>', esc_attr( $update_link_url ), $update_link_text );
                }
            }

            return $links;
        } // check_for_update_link()

        /**
         * Process the manual check for update if check for update is clicked on the plugins page.
         *
         * @since  1.0.0
         * @access public
         */
        public function process_manual_update_check() {

            if ( isset( $_GET['wcsl_check_for_update'], $_GET['wcsl_slug'] ) && $_GET['wcsl_slug'] == $this->slug && current_user_can( 'update_plugins' ) && check_admin_referer( 'wcsl_check_for_update' ) ) {

                // Check for updates
                $server_response = $this->server_request();

                if ( $this->check_license( $server_response ) ) {

                    $plugin_update_info = $server_response->software_details;

                    if ( isset( $plugin_update_info ) && is_object( $plugin_update_info ) ) {

                        if ( version_compare( (string) $plugin_update_info->new_version, (string) $this->version, '>' ) ) {

                            $update_available = true;

                        } else {

                            $update_available = false;
                        }
                    } else {

                        $update_available = false;
                    }

                    $status = ( $update_available == null ) ? 'no' : 'yes';

                    wp_redirect(
                        add_query_arg(
                            array(
                                'wcsl_update_check_result' => $status,
                                'wcsl_slug'                => $this->slug,
                            ),
                            self_admin_url( 'plugins.php' )
                        )
                    );
                }
            }
        } // process_manual_update_check()

        /**
         * Out the results of the manual check
         *
         * @since  1.0.0
         * @access public
         */
        public function output_manual_update_check_result() {

            if ( isset( $_GET['wcsl_update_check_result'], $_GET['wcsl_slug'] ) && ( $_GET['wcsl_slug'] == $this->slug ) ) {

                $check_result = $_GET['wcsl_update_check_result'];

                switch ( $check_result ) {
                    case 'no':
                        $admin_notice = __( 'This plugin is up to date. ', $this->text_domain );
                        break;
                    case 'yes':
                        $admin_notice = sprintf( __( 'An update is available for %s.', $this->text_domain ), $this->plugin_nice_name );
                        break;
                    default:
                        $admin_notice = __( 'Unknown update status.', $this->text_domain );
                        break;
                }

                printf( '<div class="updated notice is-dismissible"><p>%s</p></div>', apply_filters( 'wcsl_manual_check_message_result_' . $this->slug, $admin_notice, $check_result ) );
            }
        } // output_manual_update_check_result()

        /**
         * This is for internal purposes to ensure that during development the HTTP requests go through
         * due to security features in the WordPress HTTP API.
         *
         * Source for this solution: Plugin Update Checker Library 3387.1 by Janis Elsts
         *
         * @since  1.0.0
         * @access public
         *
         * @param bool   $allow
         * @param string $host
         *
         * @return bool
         */
        public function fix_update_host( $allow, $host ) {

            if ( strtolower( $host ) === strtolower( $this->license_server_url ) ) {
                return true;
            }

            return $allow;
        } //fix_update_host()

        /**
         * Class logger so that we can keep our debug and logging information cleaner
         *
         * @since  1.0.0
         * @access public
         *
         * @param mixed - the data to go to the error log
         */
        private function log( $data ) {

            if ( is_array( $data ) || is_object( $data ) ) {
                error_log( __CLASS__ . ' : ' . print_r( $data, true ) );
            } else {
                error_log( __CLASS__ . ' : ' . $data );
            }
        } // log()

        /**
         * Add the admin menu to the dashboard
         *
         * @since  1.0.0
         * @access public
         */
        public function add_license_menu() {

            $page = add_options_page(
                sprintf( __( '%s License', $this->text_domain ), $this->plugin_nice_name ),
                sprintf( __( '%s License', $this->text_domain ), $this->plugin_nice_name ),
                'manage_options',
                $this->slug . '_license_manager',
                array( $this, 'load_license_page' )
            );
        } // add_license_menu()

        /**
         * Load settings for the admin screens so users can input their license key
         *
         * Utilizes the WordPress Settings API to implment this
         *
         * @since  1.0.0
         * @access public
         */
        public function add_license_settings() {

            register_setting( $this->option_name, $this->option_name, array( $this, 'validate_license' ) );

            // License key section
            add_settings_section(
                $this->slug . '_license_activation',
                __( 'License Activation', $this->text_domain ),
                array( $this, 'license_activation_section_callback' ),
                $this->option_name
            );

            // License key
            add_settings_field(
                'license_key',
                __( 'License key', $this->text_domain ),
                array( $this, 'license_key_field' ),
                $this->option_name,
                $this->slug . '_license_activation'
            );

            // License status
            add_settings_field(
                'license_status',
                __( 'License Status', $this->text_domain ),
                array( $this, 'license_status_field' ),
                $this->option_name,
                $this->slug . '_license_activation'
            );

            // License expires
            add_settings_field(
                'license_expires',
                __( 'License Expires', $this->text_domain ),
                array( $this, 'license_expires_field' ),
                $this->option_name,
                $this->slug . '_license_activation'
            );

            // Deactivate license checkbox
            add_settings_field(
                'deactivate_license',
                __( 'Deactivate license', $this->text_domain ),
                array( $this, 'license_deactivate_field' ),
                $this->option_name,
                $this->slug . '_license_activation'
            );
        } // add_license_page()

        /**
         * License page output call back function
         *
         * @since  1.0.0
         * @access public
         */
        public function load_license_page() {
            ?>
            <div class='wrap'>
                <h2><?php printf( __( '%s License Manager', $this->text_domain ), $this->plugin_nice_name ); ?></h2>
                <form action='options.php' method='post'>
                    <div class="main">
                        <div class="notice update">
                            <?php printf( __( 'Please Note: If your license is active on another website you will need to deactivate this in your wcvendors.com my downloads page before being able to activate it on this site.  IMPORTANT:  If this is a development or a staging site dont activate your license.  Your license should ONLY be activated on the LIVE WEBSITE you use %s on.', $this->text_domain ), $this->plugin_nice_name, $this->plugin_nice_name ); ?>
                        </div>

                        <?php
                        // settings_errors();
                        ?>

                        <?php
                        settings_fields( $this->option_name );
                        do_settings_sections( $this->option_name );
                        submit_button( __( 'Save Changes', $this->text_domain ) );
                        ?>
                    </div>
                </form>
            </div>

            <?php
        } // license_page()

        /**
         * License activation settings section callback
         *
         * @since  1.0.0
         * @access public
         */
        public function license_activation_section_callback() {

            echo '<p>' . __( 'Please enter your license key to activate automatic updates and verify your support.', $this->text_domain ) . '</p>';
        } // license_activation_section_callback ()

        /**
         * License key field callback
         *
         * @since  1.0.0
         * @access public
         */
        public function license_key_field() {
            $value = ( isset( $this->license_details['license_key'] ) ) ? $this->license_details['license_key'] : '';
            echo '<input type="text" id="license_key" name="' . $this->option_name . '[license_key]" value="' . $value . '" />';
        } // license_key_field()

        /**
         * License acivated field
         *
         * @since  1.0.0
         * @access public
         */
        public function license_status_field() {

            // $activated       = $this->license_details[ 'license_status' ];
            $license_labels = $this->license_status_types();
            _e( $license_labels[ $this->license_details['license_status'] ] );
        } // license_status_field()

        /**
         * License acivated field
         *
         * @since  1.0.0
         * @access public
         */
        public function license_expires_field() {
            echo $this->license_details['license_expires'];
        }

        /**
         * License deactivate checkbox
         *
         * @since  1.0.0
         * @access public
         */
        public function license_deactivate_field() {

            echo '<input type="checkbox" id="deactivate_license" name="' . $this->option_name . '[deactivate_license]" />';
        } // license_deactivate_field()

        /**
         * Validate the license key information sent from the form.
         *
         * @since  1.0.0
         * @access public
         *
         * @param array $input the input passed from the request
         */
        public function validate_license( $input ) {

            $options = $this->license_details;
            $type    = null;
            $message = null;
            $expires = '';

            foreach ( $options as $key => $value ) {

                if ( 'license_key' === $key ) {

                    if ( 'active' === $this->get_license_status() ) {
                        continue;
                    }

                    if ( ! array_key_exists( 'deactivate_license', $input ) || 'deactivated' !== $this->get_license_status() ) {

                        $this->license_details['license_key'] = $input[ $key ];
                        $response                             = $this->server_request( 'activate' );

                        if ( $response !== null ) {

                            if ( $this->check_response_status( $response ) ) {

                                $options[ $key ]            = $input[ $key ];
                                $options['license_status']  = $response->status;
                                $options['license_expires'] = $response->expires;

                                if ( $response->status === 'valid' || $response->status === 'active' ) {
                                    $type    = 'updated';
                                    $message = __( 'License activated.', $this->text_domain );
                                } else {
                                    $type    = 'error';
                                    $message = $response->message;
                                }
                            } else {
                                $type    = 'error';
                                $message = __( 'Invalid License', $this->text_domain );
                            }

                            add_settings_error(
                                $this->option_name,
                                esc_attr( 'settings_updated' ),
                                $message,
                                $type
                            );

                            $options[ $key ] = $input[ $key ];
                        }
                    }

                    $options[ $key ] = $input[ $key ];

                } elseif ( array_key_exists( $key, $input ) && 'deactivate_license' === $key ) {

                    // Only run this code if the license hasn't expired
                    if ( $this->license_details['license_status'] !== 'expired' ) {

                        $response = $this->server_request( 'deactivate' );

                        if ( $response !== null ) {

                            if ( $this->check_response_status( $response ) ) {
                                $options[ $key ]            = $input[ $key ];
                                $options['license_status']  = $response->status;
                                $options['license_expires'] = $response->expires;

                                if ( $response->status !== 'expired' ) {
                                    $message = __( 'License Deactivated', $this->text_domain );
                                    $type    = 'updated';
                                }
                            } else {

                                $type    = 'updated';
                                $message = __( 'Unable to deactivate license. Please deactivate on the store.', $this->text_domain );

                            }

                            add_settings_error(
                                $this->option_name,
                                esc_attr( 'settings_updated' ),
                                $message,
                                $type
                            );
                        }
                    }
                } elseif ( 'license_status' === $key ) {

                    if ( empty( $options['license_status'] ) ) {
                        $options['license_status'] = 'inactive';
                    } else {
                        $options['license_status'] = $options['license_status'];
                    }
                } elseif ( 'license_expires' === $key ) {

                    if ( empty( $options['license_expires'] ) ) {
                        $options['license_expires'] = '';
                    } else {
                        $options['license_expires'] = date( 'Y-m-d', strtotime( $options['license_expires'] ) );
                    }
                }
            }

            return $options;
        } // validate_license()

        /**
         * The available license status types
         *
         * @since  1.0.0
         * @access public
         */
        public function license_status_types() {

            return apply_filters(
                'wcsl_license_status_types',
                array(
                    'valid'           => __( 'Valid', $this->text_domain ),
                    'deactivated'     => __( 'Deactivated', $this->text_domain ),
                    'max_activations' => __( 'Max Activations reached', $this->text_domain ),
                    'invalid'         => __( 'Invalid', $this->text_domain ),
                    'inactive'        => __( 'Inactive', $this->text_domain ),
                    'active'          => __( 'Active', $this->text_domain ),
                    'expiring'        => __( 'Expiring', $this->text_domain ),
                    'expired'         => __( 'Expired', $this->text_domain ),
                )
            );
        } // software_types()


        /**
         * --------------------------------------------------------------------------
         * Getters
         * --------------------------------------------------------------------------
         *
         * Methods for getting object properties
         */

        /**
         * Get the license setatus
         *
         * @since  1.0.0
         * @access public
         */
        public function get_license_status() {

            return $this->license_details['license_status'];
        } // get_license_status()

        /**
         * Get the license key
         *
         * @since  1.0.0
         * @access public
         */
        public function get_license_key() {

            return $this->license_details['license_key('];
        } // get_license_key()

        /**
         * Get the license expiry
         *
         * @since  1.0.0
         * @access public
         */
        public function get_license_expires() {

            return $this->license_details['license_expires'];
        } // get_license_expires()


        /**
         * --------------------------------------------------------------------------
         * Setters
         * --------------------------------------------------------------------------
         *
         * Methods to set the object properties for this instance. This does not
         * interact with the database.
         */

        /**
         * Set the license status
         *
         * @since  1.0.0
         * @access public
         *
         * @param string $license_status
         */
        public function set_license_status( $license_status ) {

            $this->license_details['license_status'] = $license_status;
        } // set_license_status()

        /**
         * Set the license key
         *
         * @since  1.0.0
         * @access public
         *
         * @param string $license_key
         */
        public function set_license_key( $license_key ) {

            $this->license_details['license_key'] = $license_key;
        } // set_license_key()

        /**
         * Set the license expires
         *
         * @since  1.0.0
         * @access public
         *
         * @param string $license_expires
         */
        public function set_license_expires( $license_expires ) {

            $this->license_details['license_expires'] = $license_expires;
        } // set_license_expires()

        public function save() {

            update_option( $this->option_name, $this->license_details );
        } // save()

    } // WC_Software_License_Client

endif;
