'modal-popups', * 'item_name' => 'Demfilz Modal Popups Pro', * 'option' => 'dmpop_license', // unique per plugin * 'text_domain' => 'demfilz-modal-popups', * ) ); * if ( $license->is_active() ) { /* unlock Pro *\/ } * * @package DemfilzShared */ defined( 'ABSPATH' ) || exit; if ( ! class_exists( 'Demfilz_License_Client' ) ) : // phpcs:disable WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedClassFound -- Shared cross-plugin Demfilz class (studio prefix), bundled into each plugin. /** * Per-product licence client. */ class Demfilz_License_Client { // phpcs:enable WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedClassFound const CHECK_INTERVAL = DAY_IN_SECONDS; const GRACE = WEEK_IN_SECONDS; /** * Config: product, item_name, option, text_domain, api_base, return_url. * * @var array */ private $cfg; /** * Constructor. * * @param array $cfg Config. */ public function __construct( $cfg ) { $this->cfg = wp_parse_args( $cfg, array( 'product' => '', 'item_name' => 'Demfilz Pro', 'option' => 'demfilz_license', 'text_domain' => 'default', 'api_base' => 'https://api.demfilz.com/auto-order', 'return_url' => '', 'version' => '1.0.0', ) ); } /** The licensing service base URL (filterable). */ public function api_base() { return apply_filters( 'demfilz_license_api_base', $this->cfg['api_base'], $this->cfg['product'] ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Intentional shared Demfilz cross-plugin hook name. } /** Hosted Stripe checkout URL for this product's subscription. */ public function checkout_url() { $url = add_query_arg( array( 'product' => rawurlencode( $this->cfg['product'] ), 'site' => rawurlencode( home_url() ), ), $this->api_base() . '/subscribe' ); return apply_filters( 'demfilz_license_checkout_url', $url, $this->cfg['product'] ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Intentional shared Demfilz cross-plugin hook name. } /** Stripe customer-portal / account URL. */ public function manage_url() { return apply_filters( 'demfilz_license_manage_url', $this->api_base() . '/manage', $this->cfg['product'] ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Intentional shared Demfilz cross-plugin hook name. } /** Customer account portal URL. */ public function account_url() { return 'https://api.demfilz.com/account/'; } /** Stored licence record. */ public function get() { $defaults = array( 'key' => '', 'status' => 'inactive', 'plan' => 'basic', 'last_check' => 0, 'last_good' => 0, 'expires' => 0, 'message' => '', ); return wp_parse_args( get_option( $this->cfg['option'], array() ), $defaults ); } /** Whether Pro is unlocked (cached, with a grace window if offline). */ public function is_active() { $lic = $this->get(); if ( '' === $lic['key'] ) { return false; } if ( $lic['last_check'] < ( time() - self::CHECK_INTERVAL ) ) { $lic = $this->validate( $lic['key'] ); } if ( 'active' === $lic['status'] ) { return true; } if ( 'unreachable' === $lic['status'] && $lic['last_good'] > ( time() - self::GRACE ) ) { return true; } return false; } /** * Validate a key against the service and persist the result. * * @param string $key Licence key. * @return array Licence record. */ public function validate( $key ) { $key = trim( sanitize_text_field( $key ) ); $lic = $this->get(); $lic['key'] = $key; $lic['last_check'] = time(); if ( '' === $key ) { $lic['status'] = 'inactive'; $lic['plan'] = 'basic'; $lic['message'] = ''; update_option( $this->cfg['option'], $lic ); return $lic; } $response = wp_remote_post( $this->api_base() . '/validate', array( 'timeout' => 15, 'body' => array( 'license_key' => $key, 'product' => $this->cfg['product'], 'site' => home_url(), 'version' => $this->cfg['version'], ), ) ); if ( is_wp_error( $response ) ) { $lic['status'] = 'unreachable'; $lic['message'] = $response->get_error_message(); update_option( $this->cfg['option'], $lic ); return $lic; } $code = (int) wp_remote_retrieve_response_code( $response ); $body = json_decode( wp_remote_retrieve_body( $response ), true ); if ( 200 === $code && is_array( $body ) && ! empty( $body['active'] ) ) { $lic['status'] = 'active'; $lic['plan'] = 'pro'; $lic['expires'] = isset( $body['expires'] ) ? absint( $body['expires'] ) : 0; $lic['last_good'] = time(); $lic['message'] = ''; } else { $lic['status'] = 'invalid'; $lic['plan'] = 'basic'; $lic['message'] = ( is_array( $body ) && isset( $body['message'] ) ) ? sanitize_text_field( $body['message'] ) : 'Licence key was not accepted.'; } update_option( $this->cfg['option'], $lic ); return $lic; } /** Remove the licence locally. */ public function clear() { update_option( $this->cfg['option'], array( 'key' => '', 'status' => 'inactive', 'plan' => 'basic', 'last_check' => time(), 'last_good' => 0, 'expires' => 0, 'message' => '', ) ); } /** * Render a complete Upgrade / Licence settings panel. Handles its own * form POST (call early on your settings page, before output). * * @return void */ public function render_panel() { $opt = $this->cfg['option']; if ( isset( $_POST[ $opt . '_action' ] ) && current_user_can( 'manage_options' ) ) { check_admin_referer( $opt . '_nonce' ); $action = sanitize_key( wp_unslash( $_POST[ $opt . '_action' ] ) ); if ( 'save' === $action ) { $this->validate( sanitize_text_field( wp_unslash( $_POST[ $opt . '_key' ] ?? '' ) ) ); } elseif ( 'clear' === $action ) { $this->clear(); } } $lic = $this->get(); $active = $this->is_active(); $name = $this->cfg['item_name']; ?>