# learnpress/4.4.9/inc/Services/AddonService.php

LearnPress – WordPress LMS Plugin for Create and Sell Online Courses, version 4.4.9. 557 lines.

- Page: https://pluginprobe.com/plugins/learnpress/4.4.9/code/inc/Services/AddonService.php
- Raw: https://pluginprobe.com/plugins/learnpress/4.4.9/raw/inc/Services/AddonService.php
- Modified: 2026-09-27T10:32:46+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/learnpress/4.4.9/code/inc/Services/AddonService.php#L10-L20`.

```php
<?php

namespace LearnPress\Services;

use DateTime;
use Exception;
use LearnPress\Helpers\LPDateTime;
use LearnPress\Helpers\Singleton;
use LP_Cache;
use LP_Debug;
use LP_Helper;
use LP_Settings;
use LP_WP_Filesystem;
use Plugin_Upgrader;
use stdClass;
use Throwable;
use WP_Ajax_Upgrader_Skin;

defined( 'ABSPATH' ) || exit;

/**
 * Class AddonService
 *
 * Manage LearnPress add-ons: list, install, update, activate, deactivate,
 * purchase code validation.
 *
 * @package LearnPress\Services
 * @change from LP_Manager_Addons class old
 * @since 4.2.1
 * @version 1.0.1
 */
class AddonService {
	use Singleton;

	/**
	 * @var string list addons config on the github
	 */
	public $url_list_addons = 'https://learnpress.github.io/learnpress/version-addons.json';
	/**
	 * @var string Link download plugin from ThimPress.
	 */
	private $link_addon_action = 'https://updates.thimpress.com/thim-addon-market/download-addon';
	/**
	 * @var string Link get info addons purchased.
	 */
	public $link_addons_purchased = 'https://updates.thimpress.com/thim-addon-market/info-addons-purchased';
	/**
	 * @var string Link active site.
	 */
	private $link_active_site = 'https://updates.thimpress.com/thim-addon-market/active-site';
	/**
	 * @var string Link download plugin from org.
	 */
	public $link_org = 'https://downloads.wordpress.org/plugin/';
	/**
	 * @var WP_Ajax_Upgrader_Skin $upgrader_skin
	 */
	public $upgrader_skin;
	/**
	 * @var Plugin_Upgrader $plugin_upgrader
	 */
	public $plugin_upgrader;
	/**
	 * @var string Option key stores purchase codes of addons.
	 */
	public $key_purchase_addons = 'purchase_addons';

	/**
	 * Constructor
	 */
	protected function __construct() {
		include_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
		include_once ABSPATH . 'wp-admin/includes/plugin-install.php';

		$this->upgrader_skin   = new WP_Ajax_Upgrader_Skin();
		$this->plugin_upgrader = new Plugin_Upgrader( $this->upgrader_skin );
	}

	/**
	 * Init hooks.
	 *
	 * @return void
	 */
	public function init(): void {}

	/**
	 * Get addons data from the remote source.
	 *
	 * @return object
	 */
	public function get_remote_data(): object {
		$data = new stdClass();

		try {
			$lp_cache  = new LP_Cache( true );
			$key_cache = 'lp_addons_data';
			$dat_cache = $lp_cache->get_cache( $key_cache );
			if ( false !== $dat_cache ) {
				return json_decode( wp_json_encode( $dat_cache ) );
			}

			if ( ! empty( $this->url_list_addons ) ) {
				$response = wp_remote_get(
					$this->url_list_addons,
					array(
						'timeout' => 30,
					)
				);

				if ( ! is_wp_error( $response )
					&& 200 === wp_remote_retrieve_response_code( $response ) ) {
					$data = LP_Helper::json_decode( wp_remote_retrieve_body( $response ) );
					$lp_cache->set_cache( $key_cache, $data, 5 * MINUTE_IN_SECONDS );
				} else {
					throw new Exception( $response->get_error_message() );
				}
			}
		} catch ( Throwable $e ) {
			LP_Debug::error_log( $e );
		}

		return $data;
	}

	/**
	 * Get list addons from file local - for test.
	 *
	 * @return object
	 * @throws Exception When the local data file cannot be read.
	 */
	public function get_local_data(): object {
		$data = new stdClass();
		$file = LP_PLUGIN_PATH . 'config/addons-data.json';

		if ( ! file_exists( $file ) || ! is_readable( $file ) ) {
			return $data;
		}

		$content = file_get_contents( $file );
		if ( false === $content ) {
			return $data;
		}

		return LP_Helper::json_decode( $content );
	}

	/**
	 * Get list addons with purchase info attached.
	 *
	 * @return object List addons (stdClass map keyed by slug).
	 * @throws Exception
	 */
	public function get_addons(): object {
		//$addons = $this->get_local_data();
		$addons = $this->get_remote_data();

		$addons = $this->get_addons_purchased( $addons );

		return apply_filters( 'learn-press/data/addons', $addons );
	}

	/**
	 * Get list addons purchased and attach purchase_info to each addon.
	 *
	 * @param object $addons List addons keyed by slug.
	 *
	 * @return object
	 * @throws Exception
	 */
	public function get_addons_purchased( object $addons ): object {
		$addons_purchase = LP_Settings::get_option( $this->key_purchase_addons, [] );
		if ( empty( $addons_purchase ) ) {
			return $addons;
		}

		$lp_cache     = new LP_Cache( true );
		$key_cache    = 'addons_purchased_info';
		$addons_cache = $lp_cache->get_cache( $key_cache );
		if ( false !== $addons_cache ) {
			return json_decode( wp_json_encode( $addons_cache ) );
		}

		$args = [
			'method'     => 'POST',
			'body'       => [
				'addons_purchase' => $addons_purchase,
			],
			'timeout'    => 30,
			'user-agent' => site_url(),
		];

		$result = wp_remote_post( $this->link_addons_purchased, $args );
		if ( is_wp_error( $result ) ) {
			throw new Exception( $result->get_error_message() );
		}

		$data_str = wp_remote_retrieve_body( $result );
		if ( preg_match( '/^Error.*/', $data_str ) ) {
			throw new Exception( $data_str );
		}

		$data = LP_Helper::json_decode( $data_str );

		foreach ( $addons as $key => $addon ) {
			if ( isset( $data->{$key} ) ) {
				$addons->{$key}->purchase_info = $data->{$key};
			}
		}

		$lp_cache->set_cache( $key_cache, $addons );

		return $addons;
	}

	/**
	 * Validate a purchase code with the add-ons service before storing it.
	 *
	 * @param string $addon_slug    Add-on slug.
	 * @param string $purchase_code Purchase code.
	 *
	 * @return object Purchase information returned by the service.
	 * @throws Exception When the code cannot be validated.
	 */
	public function validate_and_save_purchase_code( string $addon_slug, string $purchase_code ): object {
		$purchase_code = trim( $purchase_code );

		if ( empty( $addon_slug ) || empty( $purchase_code ) ) {
			throw new Exception( __( 'Purchase code is invalid!', 'learnpress' ) );
		}

		$response = wp_remote_post(
			$this->link_addons_purchased,
			array(
				'method'     => 'POST',
				'body'       => array(
					'addons_purchase' => array(
						$addon_slug => $purchase_code,
					),
				),
				'timeout'    => 30,
				'user-agent' => site_url(),
			)
		);

		if ( is_wp_error( $response ) ) {
			throw new Exception( $response->get_error_message() );
		}

		$response_body = wp_remote_retrieve_body( $response );
		if ( empty( $response_body ) || preg_match( '/^Error.*/', $response_body ) ) {
			throw new Exception( __( 'Purchase code is invalid!', 'learnpress' ) );
		}

		$data = LP_Helper::json_decode( $response_body );
		if ( ! is_object( $data ) || ! isset( $data->{$addon_slug} ) || ! is_object( $data->{$addon_slug} ) ) {
			throw new Exception( __( 'Purchase code is invalid!', 'learnpress' ) );
		}

		$key_purchase                = LP_Settings::get_option( $this->key_purchase_addons, array() );
		$key_purchase[ $addon_slug ] = $purchase_code;
		LP_Settings::update_option( $this->key_purchase_addons, $key_purchase );

		$this->clear_addons_purchased_cache();

		return $data->{$addon_slug};
	}

	/**
	 * Mask a purchase code with 16 asterisks, keeping its final four characters visible.
	 *
	 * @param string $purchase_code Purchase code.
	 *
	 * @return string
	 */
	public static function mask_purchase_code( string $purchase_code ): string {
		$purchase_code = trim( $purchase_code );

		if ( empty( $purchase_code ) ) {
			return '';
		}

		return str_repeat( '*', 16 ) . substr( $purchase_code, -4 );
	}

	/**
	 * Get the license status for an expiration date.
	 *
	 * @param string $date_expire  Expiration date.
	 * @param string $current_date Current date in Y-m-d format.
	 *
	 * @return string
	 */
	public static function get_license_status( string $date_expire, string $current_date = '' ): string {
		if ( empty( $date_expire ) ) {
			return 'active';
		}

		$current_date = $current_date ?: gmdate( 'Y-m-d' );

		return strtotime( $date_expire ) <= strtotime( $current_date ) ? 'expired' : 'active';
	}

	/**
	 * Download addon from ThimPress.
	 *
	 * @param array  $addon         Addon data.
	 * @param string $purchase_code Purchase code.
	 *
	 * @return string Path to downloaded zip file.
	 * @throws Exception
	 */
	public function download_from_thimpress( array $addon = [], string $purchase_code = '' ): string {
		$lp_file_system = LP_WP_Filesystem::instance();
		$link_download  = $this->link_addon_action;
		$args           = [
			'method'     => 'POST',
			'body'       => [
				'addon'   => $addon['slug'],
				'version' => 'lastest',
			],
			'user-agent' => site_url(),
		];

		// For addon must buy.
		if ( 0 == $addon['is_free'] ) {
			$key_purchase                   = LP_Settings::get_option( $this->key_purchase_addons, [] );
			$key_purchase[ $addon['slug'] ] = $purchase_code;
			LP_Settings::update_option( $this->key_purchase_addons, $key_purchase );

			$args['body']['purchase_code'] = $purchase_code;
		}

		$result = wp_remote_post( $link_download, $args );
		if ( is_wp_error( $result ) ) {
			throw new Exception( $result->get_error_message() );
		}

		$data = wp_remote_retrieve_body( $result );
		if ( preg_match( '/^Error.*/', $data ) ) {
			throw new Exception( $data );
		}

		// Create file temp zip addon to install with.
		$wp_upload_dir = wp_upload_dir( null, false );
		$name          = 'addon.zip';
		$path_file     = $wp_upload_dir['basedir'] . DIRECTORY_SEPARATOR . $name;
		$lp_file_system->put_contents( $path_file, $data );

		return $path_file;
	}

	/**
	 * Install plugin.
	 *
	 * @param array  $addon   Addon data.
	 * @param string $package The full local path or URI of the package.
	 *
	 * @return void
	 * @throws Exception
	 */
	public function install( array $addon = [], string $package = '' ) {
		$result_install = $this->plugin_upgrader->install( $package );
		if ( is_wp_error( $result_install ) ) {
			throw new Exception( $result_install->get_error_message() );
		} elseif ( ! $result_install ) {
			throw new Exception( __( 'Install failed!', 'learnpress' ) );
		}

		$result_active = activate_plugin( $addon['basename'] );
		if ( is_wp_error( $result_active ) ) {
			throw new Exception( $result_active->get_error_message() );
		}

		$this->clear_addons_purchased_cache();
	}

	/**
	 * Update plugin.
	 *
	 * @param array  $addon   Addon data.
	 * @param string $package The full local path or URI of the package.
	 *
	 * @throws Exception
	 */
	public function update( array $addon = [], string $package = '' ) {
		$is_activate = is_plugin_active( $addon['basename'] );
		// Must call this function to upgrade success.
		wp_update_plugins();

		$args_upgrade = [
			'package'                     => $package,
			'destination'                 => WP_PLUGIN_DIR,
			'clear_destination'           => false,
			'clear_working'               => true,
			'hook_extra'                  => [],
			'abort_if_destination_exists' => false,
		];
		$result       = $this->plugin_upgrader->run( $args_upgrade );
		if ( ! $result ) {
			throw new Exception( __( 'Update failed!', 'learnpress' ) );
		}

		if ( $is_activate ) {
			$this->activate( $addon );
		}

		$this->clear_addons_purchased_cache();
	}

	/**
	 * Activate plugin.
	 *
	 * @param array $addon Addon data.
	 *
	 * @return bool|int|true|\WP_Error
	 * @throws Exception
	 */
	public function activate( array $addon = [] ) {
		if ( isset( $addon['dependency'] ) ) {
			foreach ( $addon['dependency'] as $addon_slug => $addon_label ) {
				if ( ! is_plugin_active( $addon_slug ) ) {
					throw new Exception( sprintf( 'Please activate "%s" plugin before activate this add-on', $addon_label ) );
				}
			}
		}

		$result_active = activate_plugin( $addon['basename'] ?? '' );
		if ( is_wp_error( $result_active ) ) {
			throw new Exception( $result_active->get_error_message() );
		}

		return $result_active;
	}

	/**
	 * Deactivate plugin.
	 *
	 * @param array $addon Addon data.
	 *
	 * @return void
	 */
	public function deactivate( array $addon = [] ) {
		deactivate_plugins( $addon['basename'] ?? '' );
	}

	/**
	 * Clear cache of addons purchased info.
	 *
	 * @return void
	 */
	private function clear_addons_purchased_cache(): void {
		$lp_cache = new LP_Cache( true );
		$lp_cache->clear( 'addons_purchased_info' );
	}

	/**
	 * Active site if install plugin via upload zip has "purchase code".
	 *
	 * @param string $addon_slug Addon slug.
	 * @param string $purchase_code Purchase code.
	 *
	 * @return void
	 * @throws Exception
	 */
	public function active_site( string $addon_slug, string $purchase_code ) {
		$args = [
			'method'     => 'POST',
			'body'       => [
				'addon'         => $addon_slug,
				'purchase_code' => $purchase_code,
			],
			'user-agent' => site_url(),
		];

		$result = wp_remote_post( $this->link_active_site, $args );
		if ( is_wp_error( $result ) ) {
			throw new Exception( $result->get_error_message() );
		}

		$data = wp_remote_retrieve_body( $result );
		if ( preg_match( '/^Error.*/', $data ) ) {
			throw new Exception( $data );
		}

		// Save keys purchase code of addons to table WP Options.
		$key_purchases                = LP_Settings::get_option( $this->key_purchase_addons, [] );
		$key_purchases[ $addon_slug ] = $purchase_code;
		LP_Settings::update_option( $this->key_purchase_addons, $key_purchases );
	}

	/**
	 * Get list addon have new version.
	 *
	 * @return array
	 */
	public function list_addon_new_version(): array {
		$addons_new_version = [];

		try {
			$addons  = $this->get_addons();
			$plugins = get_plugins();

			foreach ( $addons as $addon ) {
				if ( isset( $plugins[ $addon->basename ] ) ) {
					$version = $plugins[ $addon->basename ]['Version'];
					if ( version_compare( $version, $addon->version, '<' ) ) {
						$addons_new_version[] = $addon;
					}
				}
			}
		} catch ( Throwable $e ) {
			LP_Debug::error_log( $e );
		}

		return $addons_new_version;
	}

	/**
	 * Check addons purchased need extend.
	 *
	 * @return bool
	 * @since 4.2.5.9
	 * @version 1.0.1
	 */
	public function check_addons_purchased_need_extend(): bool {
		try {
			$addons = $this->get_addons();

			foreach ( $addons as $addon ) {
				if ( isset( $addon->purchase_info ) ) {
					$addon_purchased  = $addon->purchase_info;
					$date_expired_str = $addon_purchased->date_expire ?? '';
					if ( empty( $date_expired_str ) ) {
						continue;
					}

					$date_expired          = new DateTime( $date_expired_str );
					$date_now              = new DateTime( gmdate( LPDateTime::FORMAT_MYSQL ) );
					$date_diff             = date_diff( $date_now, $date_expired );
					$number_days_remaining = $date_diff->days;
					if ( $date_diff->invert ) {
						$number_days_remaining = 0;
					}

					if ( $number_days_remaining <= 60 ) {
						return true;
					}
				}
			}
		} catch ( Throwable $e ) {
			LP_Debug::var_dump( $e );
		}

		return false;
	}
}

```
