*/ public static function get_post_access_state($post = null, int $user_id = 0): array { $resource_post = get_post($post); $uid = $user_id > 0 ? $user_id : get_current_user_id(); $pay_meta = function_exists('aksrt_get_pay_meta') ? aksrt_get_pay_meta($resource_post) : AKSRT_Domain_Model::default_pay_meta(); $pay_type = AKSRT_Domain_Model::normalize_pay_type((string) ($pay_meta['pay_type'] ?? AKSRT_Domain_Model::PAY_TYPE_NO)); $priced = function_exists('aksrt_get_user_pay_price') ? aksrt_get_user_pay_price($resource_post, $uid) : null; $pay_modo = is_array($priced) ? ((($priced['modo'] ?? '0') === 'points') ? 'points' : '0') : ((($pay_meta['pay_modo'] ?? '0') === 'points') ? 'points' : '0'); $price = is_array($priced) ? (float) ($priced['amount'] ?? 0) : (($pay_modo === 'points') ? (float) ($pay_meta['points_price'] ?? 0) : (float) ($pay_meta['price'] ?? 0)); $paid_settings = class_exists('AKSRT_Settings') ? AKSRT_Settings::get_paid_content_settings() : [ 'download_login' => true, 'download_credit' => true, 'download_captcha' => false, ]; $state = [ 'post_id' => $resource_post instanceof WP_Post ? (int) $resource_post->ID : 0, 'pay_type' => $pay_type, 'pay_modo' => $pay_modo, 'price' => $price, 'need_login' => false, 'need_purchase' => false, 'need_vip' => false, 'require_captcha' => false, 'has_paid_access' => false, 'can_view' => true, 'can_download' => true, 'download_cost' => 0, 'pay_limit' => 0, 'user_vip_level' => 0, ]; if (!($resource_post instanceof WP_Post)) { $state['can_view'] = false; $state['can_download'] = false; return $state; } if ($pay_type === AKSRT_Domain_Model::PAY_TYPE_NO) { $state['has_paid_access'] = true; return $state; } $has_free_access = $price <= 0 && $pay_type !== AKSRT_Domain_Model::PAY_TYPE_VIP; $pay_limit = (int) ($pay_meta['pay_limit'] ?? 0); if (!in_array($pay_limit, [0, 1, 2], true)) { $pay_limit = 0; } $state['pay_limit'] = $pay_limit; $has_paid_order = false; if ($uid > 0 && class_exists('AKSRT_Order')) { $has_paid_order = AKSRT_Order::get_instance()->has_paid_access( $uid, AKSRT_Domain_Model::ORDER_PRODUCT_RESOURCE, (int) $resource_post->ID ); } $has_vip_access = false; $user_vip_level = is_array($priced) ? (int) ($priced['vip_level'] ?? 0) : 0; if ($uid > 0) { if ($user_vip_level <= 0 && function_exists('aksrt_get_user_vip_level')) { $user_vip_level = aksrt_get_user_vip_level($uid); } if (function_exists('aksrt_is_vip_exempt') && aksrt_is_vip_exempt($uid)) { $has_vip_access = true; $user_vip_level = max($user_vip_level, 2); } elseif ($pay_type === AKSRT_Domain_Model::PAY_TYPE_VIP || $pay_limit > 0) { $has_vip_access = $user_vip_level >= max(1, $pay_limit); } elseif ($user_vip_level > 0) { // VIP tier pricing may zero cost; still track membership. $has_vip_access = $pay_limit <= 0 || $user_vip_level >= $pay_limit; } } $state['user_vip_level'] = $user_vip_level; // Member-only purchase restriction (Zibll pay_limit). if ($pay_limit > 0 && $pay_type !== AKSRT_Domain_Model::PAY_TYPE_NO) { $state['need_vip'] = true; $allowed_member = $uid > 0 && $user_vip_level >= $pay_limit; if ($pay_type === AKSRT_Domain_Model::PAY_TYPE_DOWNLOAD) { $download_cost = function_exists('aksrt_resource_download_cost') ? (int) aksrt_resource_download_cost($resource_post) : (int) ceil($price); // If member is free via VIP price, cost is 0. if ($allowed_member && $price <= 0) { $download_cost = 0; } $state['download_cost'] = max(0, $download_cost); $state['need_login'] = $uid <= 0; $state['need_purchase'] = $allowed_member && $state['download_cost'] > 0 && !$has_paid_order; $state['has_paid_access'] = $allowed_member && ($has_paid_order || $state['download_cost'] <= 0); $state['can_view'] = true; $state['can_download'] = $allowed_member && ($uid > 0) && ($has_paid_order || $state['download_cost'] <= 0); if (!$allowed_member) { $state['can_download'] = false; $state['has_paid_access'] = false; $state['need_purchase'] = false; } return $state; } $state['has_paid_access'] = ($allowed_member && $price <= 0) || $has_paid_order || ($allowed_member && $has_vip_access && $pay_type === AKSRT_Domain_Model::PAY_TYPE_VIP); // For reading/gallery/video with pay_limit: members still need to purchase unless free. if ($allowed_member) { $state['has_paid_access'] = $has_paid_order || $price <= 0; $state['need_purchase'] = !$state['has_paid_access']; } else { $state['has_paid_access'] = false; $state['need_purchase'] = false; } $state['can_view'] = $state['has_paid_access']; $state['can_download'] = false; return $state; } if ($pay_type === AKSRT_Domain_Model::PAY_TYPE_DOWNLOAD) { $download_cost = function_exists('aksrt_resource_download_cost') ? (int) aksrt_resource_download_cost($resource_post) : (int) ceil($price); $state['download_cost'] = max(0, $download_cost); // Zibll pay_free_logged_show: free download still requires login. $free_logged_show = !empty($paid_settings['free_logged_show']); $state['need_login'] = !empty($paid_settings['download_login']) || !empty($pay_meta['require_login']) || ($free_logged_show && $state['download_cost'] <= 0); $state['require_captcha'] = !empty($paid_settings['download_captcha']) || !empty($pay_meta['require_captcha']); // Cash mode with cost handled by orders: need purchase when no order and base price > 0. $needs_pay = $state['download_cost'] > 0 || ($pay_modo === '0' && $price > 0 && !$has_paid_order && empty($paid_settings['download_credit'])); if ($pay_modo === '0' && $price > 0 && empty($paid_settings['download_credit'])) { $state['need_purchase'] = !$has_paid_order && !($has_vip_access && $price <= 0); $state['has_paid_access'] = $has_paid_order || $price <= 0; // Download still gated by login/credit path for token; cash unlock is order-based. $state['can_view'] = true; $state['can_download'] = ($has_paid_order || $price <= 0) && (!$state['need_login'] || $uid > 0); return $state; } $state['need_purchase'] = !$has_paid_order && $state['download_cost'] > 0 && !$has_free_access; // VIP with free tier amount already reflected in download_cost. $state['has_paid_access'] = $has_paid_order || $state['download_cost'] <= 0 || $has_free_access; $state['can_view'] = true; $state['can_download'] = (!$state['need_login'] || $uid > 0) && ($has_paid_order || $state['download_cost'] <= 0 || true); // can_download true only means "may request token"; rest_request still deducts credits. return $state; } if ($pay_type === AKSRT_Domain_Model::PAY_TYPE_VIP) { $state['need_vip'] = true; $state['has_paid_access'] = $has_vip_access; $state['can_view'] = $has_vip_access; $state['can_download'] = false; return $state; } $state['need_login'] = !empty($pay_meta['require_login']); $state['need_purchase'] = !$has_free_access && !$has_paid_order; $state['has_paid_access'] = $has_free_access || $has_paid_order; // VIP free tier for reading etc. via amount. if (!$state['has_paid_access'] && $price <= 0 && $pay_type !== AKSRT_Domain_Model::PAY_TYPE_VIP) { $state['has_paid_access'] = true; $state['need_purchase'] = false; } $state['can_view'] = $state['has_paid_access'] && (!$state['need_login'] || $uid > 0); $state['can_download'] = false; return $state; } public static function can_view_paid_content($post = null, int $user_id = 0): bool { $state = self::get_post_access_state($post, $user_id); return !empty($state['can_view']); } /** * @param int|WP_Post|null $post * @return true|WP_Error */ public static function can_download_resource($post = null, int $user_id = 0) { $resource_post = get_post($post); if (!($resource_post instanceof WP_Post) || !function_exists('aksrt_is_downloadable_content') || !aksrt_is_downloadable_content($resource_post)) { return new WP_Error('aksrt_not_downloadable', __('该内容未启用下载能力。', 'aksrt')); } $state = self::get_post_access_state($resource_post, $user_id); $uid = $user_id > 0 ? $user_id : get_current_user_id(); if (($state['pay_type'] ?? '') !== AKSRT_Domain_Model::PAY_TYPE_DOWNLOAD) { return new WP_Error('aksrt_not_downloadable', __('该内容未启用下载能力。', 'aksrt')); } if (!empty($state['need_login']) && $uid <= 0) { return new WP_Error('aksrt_need_login', __('请先登录后再下载。', 'aksrt'), [ 'status' => 401, 'login_url' => wp_login_url(get_permalink($resource_post)), ]); } // Free download + pay_free_logged_show (belt-and-suspenders with state.need_login). $cost = (int) ($state['download_cost'] ?? 0); $paid_settings = class_exists('AKSRT_Settings') ? AKSRT_Settings::get_paid_content_settings() : ['download_credit' => true, 'free_logged_show' => false]; if ($cost <= 0 && !empty($paid_settings['free_logged_show']) && $uid <= 0) { return new WP_Error('aksrt_need_login', __('免费资源,请登录后下载。', 'aksrt'), [ 'status' => 401, 'login_url' => wp_login_url(get_permalink($resource_post)), ]); } // Member-only (pay_limit). if (!empty($state['need_vip']) && empty($state['has_paid_access']) && empty($state['can_download'])) { $need_level = (int) ($state['pay_limit'] ?? 1); return new WP_Error('aksrt_need_vip', sprintf( /* translators: %d: required VIP level */ __('该资源仅限 VIP%s 及以上会员购买/下载。', 'aksrt'), max(1, $need_level) ), [ 'status' => 403, 'pay_limit' => $need_level, ]); } // Cash purchase required (order path). if (!empty($state['need_purchase']) && empty($state['has_paid_access']) && $cost <= 0 && (($state['pay_modo'] ?? '0') === '0')) { return new WP_Error('aksrt_need_purchase', __('请先购买后再下载。', 'aksrt'), [ 'status' => 402, ]); } // Zibll daily free-download quota. if ($cost <= 0 && function_exists('aksrt_check_free_download_quota')) { $quota = aksrt_check_free_download_quota($uid, $cost); if (is_wp_error($quota)) { return $quota; } } $pay_modo = (string) ($state['pay_modo'] ?? '0'); $need_credit_check = $cost > 0 && ( $pay_modo === 'points' || !empty($paid_settings['download_credit']) ); if ($need_credit_check) { $balance = class_exists('AKSRT_Credits') ? AKSRT_Credits::balance($uid) : 0; if ($balance < $cost) { return new WP_Error('aksrt_credits_insufficient', __('积分不足,无法下载。', 'aksrt'), [ 'status' => 402, 'balance' => $balance, 'cost' => $cost, ]); } } return true; } public function render_hide_shortcode(array $atts, string $content = ''): string { $atts = shortcode_atts([ 'mode' => 'comment', 'message' => '', 'pid' => get_the_ID(), ], $atts, 'aksrt_hide'); $mode = sanitize_key((string) $atts['mode']); $pid = (int) $atts['pid']; $message = (string) $atts['message']; if ($pid <= 0) { return ''; } if ($this->is_unlocked($mode, $pid)) { return '
' . do_shortcode($content) . '
'; } if ($message === '') { switch ($mode) { case 'comment': $message = __('评论后查看隐藏内容', 'aksrt'); break; case 'login': $message = __('登录后查看隐藏内容', 'aksrt'); break; case 'password': $message = __('输入密码后查看隐藏内容', 'aksrt'); break; default: $message = __('此内容已隐藏', 'aksrt'); } } $wrapper_class = 'aksrt-hidden-content aksrt-hidden-content--locked aksrt-hidden-content--' . esc_attr($mode); $html = '
'; $html .= '
'; $html .= ' '; $html .= '' . esc_html($message) . ''; $html .= '
'; if ($mode === 'password') { $html .= '
'; $html .= ''; $html .= ''; $html .= '
'; } $html .= '
'; return $html; } private function is_unlocked(string $mode, int $pid): bool { switch ($mode) { case 'comment': if (!is_user_logged_in()) { return false; } return get_comments([ 'post_id' => $pid, 'user_id' => get_current_user_id(), 'count' => true, 'status' => 'approve', ]) > 0; case 'login': return is_user_logged_in(); case 'password': return !empty($_COOKIE['aksrt_hide_pwd_' . $pid]); default: return false; } } public function ajax_check_visibility(): void { check_ajax_referer('aksrt_visibility', '_nonce'); $mode = sanitize_key((string) ($_POST['mode'] ?? '')); $pid = (int) ($_POST['pid'] ?? 0); if ($pid <= 0 || !in_array($mode, ['comment', 'login', 'password'], true)) { wp_send_json_error(['message' => __('参数无效', 'aksrt')]); } if ($mode === 'password') { $password = sanitize_text_field((string) ($_POST['password'] ?? '')); $post_password = get_post_password($pid); if ($post_password === '' || $password !== $post_password) { wp_send_json_error(['message' => __('密码错误', 'aksrt')]); } setcookie('aksrt_hide_pwd_' . $pid, '1', time() + HOUR_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN, is_ssl(), true); wp_send_json_success(['message' => __('验证成功', 'aksrt')]); } if ($this->is_unlocked($mode, $pid)) { wp_send_json_success(['message' => __('已解锁', 'aksrt')]); } wp_send_json_error(['message' => __('未满足条件', 'aksrt')]); } }