/** * WCDE Pro bridge — license key storage/UI and the "PRO" admin badge. * * wcde_is_pro() defaults to FALSE here; only the separate Pro add-on (sold outside wp.org) flips * it via the 'wcde_is_pro' filter, by reading the status this file stores after a real check. * * Validation goes through a proxy on tarkva.com * * recheck() (the daily cron) tolerates a run of technical failures before changing a valid * license's status, so a brief tarkva.com outage doesn't look like a license problem to a * correctly-licensed customer. A genuine rejection from a reachable server is still immediate. * activate() always reports its real result immediately — it's a deliberate, on-demand action. */ if ( ! defined( 'ABSPATH' ) ) { exit; } if ( ! defined( 'WCDE_LICENSE_API_BASE' ) ) { define( 'WCDE_LICENSE_API_BASE', 'https://tarkva.com' ); } function wcde_is_pro() { return (bool) apply_filters( 'wcde_is_pro', false ); } class WCDE_Pro { const KEY_OPTION = 'wcde_license_key'; const STATUS_OPTION = 'wcde_license_status'; // 'valid' | 'expired' | 'error' | '' const DETAIL_OPTION = 'wcde_license_detail'; const CHECKED_OPTION = 'wcde_license_checked_at'; const FAIL_COUNT_OPTION = 'wcde_license_fail_count'; const CRON_HOOK = 'wcde_cron_license_check'; public static function store_url() { return apply_filters( 'wcde_store_url', 'https://tarkva.com' ); } public static function badge( $label = 'PRO' ) { return ' ' . esc_html( $label ) . ''; } public static function max_free_tiers() { return (int) apply_filters( 'wcde_max_free_tiers', 2 ); } public static function key() { return (string) get_option( self::KEY_OPTION, '' ); } public static function status() { return (string) get_option( self::STATUS_OPTION, '' ); } public static function detail() { return get_option( self::DETAIL_OPTION, array() ); } public static function checked_at() { return (string) get_option( self::CHECKED_OPTION, '' ); } public static function mask( $key ) { $key = (string) $key; if ( strlen( $key ) <= 4 ) { return $key ? str_repeat( '•', strlen( $key ) ) : ''; } return str_repeat( '•', max( 4, strlen( $key ) - 4 ) ) . substr( $key, -4 ); } private static function api_base() { return untrailingslashit( apply_filters( 'wcde_license_api_base', WCDE_LICENSE_API_BASE ) ); } private static function configured() { return '' !== self::api_base(); } /** @return array{ok:bool, http_code:int, data:array, message:string} */ private static function call( $action, $key ) { if ( ! self::configured() ) { return array( 'ok' => false, 'http_code' => 0, 'data' => array(), 'message' => 'License API is not configured yet.' ); } $url = self::api_base() . '/wp-json/tarkva/v1/license/' . $action . '/' . rawurlencode( $key ); $response = wp_remote_get( $url, array( 'timeout' => 12 ) ); if ( is_wp_error( $response ) ) { return array( 'ok' => false, 'http_code' => 0, 'data' => array(), 'message' => $response->get_error_message() ); } $code = (int) wp_remote_retrieve_response_code( $response ); $body = json_decode( (string) wp_remote_retrieve_body( $response ), true ); if ( ! is_array( $body ) ) { return array( 'ok' => false, 'http_code' => $code, 'data' => array(), 'message' => 'Unexpected response from the license server.' ); } if ( $code < 200 || $code >= 300 ) { $msg = $body['message'] ?? sprintf( 'License server returned HTTP %d.', $code ); return array( 'ok' => false, 'http_code' => $code, 'data' => $body, 'message' => (string) $msg ); } return array( 'ok' => true, 'http_code' => $code, 'data' => $body, 'message' => '' ); } private static function extract_license( array $data ) { if ( isset( $data['data'] ) && is_array( $data['data'] ) ) { return $data['data']; } return $data; } private static function store_result( $key, array $result ) { if ( $result['ok'] ) { $lic = self::extract_license( $result['data'] ); $exp = $lic['expiresAt'] ?? ( $lic['expires_at'] ?? null ); $status = 'valid'; if ( $exp && strtotime( (string) $exp ) && strtotime( (string) $exp ) < time() ) { $status = 'expired'; } update_option( self::KEY_OPTION, $key ); update_option( self::STATUS_OPTION, $status ); update_option( self::DETAIL_OPTION, $lic ); } else { update_option( self::STATUS_OPTION, 'error' ); update_option( self::DETAIL_OPTION, array( 'error' => $result['message'] ) ); } update_option( self::CHECKED_OPTION, current_time( 'mysql' ) ); update_option( self::FAIL_COUNT_OPTION, 0 ); } public static function activate( $key ) { $key = sanitize_text_field( wp_unslash( $key ) ); if ( '' === $key ) { return 'Enter a license key.'; } $result = self::call( 'activate', $key ); self::store_result( $key, $result ); return $result['ok'] ? 'License activated.' : ( 'Activation failed: ' . $result['message'] ); } public static function deactivate() { $key = self::key(); if ( '' !== $key ) { self::call( 'deactivate', $key ); } // best-effort delete_option( self::KEY_OPTION ); delete_option( self::STATUS_OPTION ); delete_option( self::DETAIL_OPTION ); delete_option( self::CHECKED_OPTION ); delete_option( self::FAIL_COUNT_OPTION ); } private static function grace_failures() { return max( 1, (int) apply_filters( 'wcde_license_grace_failures', 5 ) ); } public static function recheck() { $key = self::key(); if ( '' === $key ) { return; } $result = self::call( 'validate', $key ); if ( $result['ok'] ) { self::store_result( $key, $result ); return; } $technical_failure = ( 0 === $result['http_code'] || $result['http_code'] >= 500 ); if ( ! $technical_failure ) { self::store_result( $key, $result ); return; } $fails = (int) get_option( self::FAIL_COUNT_OPTION, 0 ) + 1; update_option( self::FAIL_COUNT_OPTION, $fails ); if ( $fails >= self::grace_failures() ) { self::store_result( $key, $result ); } } public static function features() { return array( 'Stock & velocity intelligence — discount slow/overstocked items earlier, comfortable sellers later', 'Unlimited discount tiers per type (free plan: only the 2 closest to the expiry deadline)', 'Cart price-split — full price above the discounted batch quantity (revenue protection)', 'Advanced notices — general/cart reminders, quantity display, custom CSS placement', 'Scheduled & automated backups', 'Revenue-recovered reporting, with an optional weekly email summary', 'Priority email support', ); } } add_action( 'init', function () { if ( ! wp_next_scheduled( WCDE_Pro::CRON_HOOK ) ) { wp_schedule_event( time() + HOUR_IN_SECONDS, 'daily', WCDE_Pro::CRON_HOOK ); } } ); add_action( WCDE_Pro::CRON_HOOK, array( 'WCDE_Pro', 'recheck' ) ); register_deactivation_hook( __FILE__, function () { wp_clear_scheduled_hook( WCDE_Pro::CRON_HOOK ); } );