0,
'expires' => 0,
'state' => 'unopened',
'expired_level' => 0,
'source' => 'none',
'has_explicit_state' => false,
'is_permanent' => false,
'current_level' => 0,
'current_expires' => 0,
'current_state' => 'unopened',
'fallback_level' => 0,
'fallback_expires' => 0,
'fallback_state' => 'none',
'has_fallback' => false,
'is_layered_upgrade' => false,
];
if ($user_id <= 0) {
$user_id = get_current_user_id();
}
if ($user_id <= 0 || !class_exists('\AKSRT\Settings\Settings')) {
return $empty;
}
$vip = \AKSRT\Settings\Settings::get_payment_vip_settings();
if (empty($vip['enabled'])) {
return $empty;
}
$levels = is_array($vip['levels'] ?? null) ? $vip['levels'] : [];
$vip1_enabled = !empty($levels[1]['enabled']);
$vip2_enabled = !empty($levels[2]['enabled']);
$has_explicit = metadata_exists('user', $user_id, 'aksrt_vip_level');
$level = min(2, max(0, (int) get_user_meta($user_id, 'aksrt_vip_level', true)));
$expires = max(0, (int) get_user_meta($user_id, 'aksrt_vip_expires', true));
$expired_level = min(2, max(0, (int) get_user_meta($user_id, 'aksrt_vip_level_expired', true)));
if ($has_explicit) {
if ($level > 0 && $expires > 0 && $expires <= time()) {
if (class_exists('\\AKSRT\\Content\\VipMembershipExpiry')) {
\AKSRT\Content\VipMembershipExpiry::process_user($user_id, 'resolver');
$level = min(2, max(0, (int) get_user_meta($user_id, 'aksrt_vip_level', true)));
$expires = max(0, (int) get_user_meta($user_id, 'aksrt_vip_expires', true));
} else {
$base_level = min(2, max(0, (int) get_user_meta($user_id, 'aksrt_vip_base_level', true)));
$base_expires = max(0, (int) get_user_meta($user_id, 'aksrt_vip_base_expires', true));
if ($base_level > 0 && ($base_expires === 0 || $base_expires > time())) {
update_user_meta($user_id, 'aksrt_vip_level', $base_level);
update_user_meta($user_id, 'aksrt_vip_expires', $base_expires);
delete_user_meta($user_id, 'aksrt_vip_base_level');
delete_user_meta($user_id, 'aksrt_vip_base_expires');
$level = $base_level;
$expires = $base_expires;
$expired_level = 0;
} else {
$expired_level = max($expired_level, $level);
update_user_meta($user_id, 'aksrt_vip_level_expired', $expired_level);
update_user_meta($user_id, 'aksrt_vip_level', 0);
update_user_meta($user_id, 'aksrt_vip_expires', 0);
delete_user_meta($user_id, 'aksrt_vip_base_level');
delete_user_meta($user_id, 'aksrt_vip_base_expires');
$level = 0;
$expires = 0;
}
}
}
$fallback_level = min(2, max(0, (int) get_user_meta($user_id, 'aksrt_vip_base_level', true)));
$fallback_expires = max(0, (int) get_user_meta($user_id, 'aksrt_vip_base_expires', true));
if ($level > 0) {
$level = $level >= 2 && $vip2_enabled ? 2 : ($vip1_enabled ? 1 : 0);
$current_state = $expires > 0 ? 'active' : 'permanent';
$fallback_state = $fallback_level > 0 ? ($fallback_expires > 0 ? 'active' : 'permanent') : 'none';
return [
'level' => $level,
'expires' => $expires,
'state' => $current_state,
'expired_level' => 0,
'source' => 'meta',
'has_explicit_state' => true,
'is_permanent' => $expires <= 0,
'current_level' => $level,
'current_expires' => $expires,
'current_state' => $current_state,
'fallback_level' => $fallback_level,
'fallback_expires' => $fallback_expires,
'fallback_state' => $fallback_state,
'has_fallback' => $fallback_level > 0,
'is_layered_upgrade' => $fallback_level > 0 && $fallback_level < $level,
];
}
return array_merge($empty, [
'state' => $expired_level > 0 ? 'expired' : 'unopened',
'expired_level' => $expired_level,
'source' => 'meta',
'has_explicit_state' => true,
'current_state' => $expired_level > 0 ? 'expired' : 'unopened',
]);
}
$paid = \AKSRT\Settings\Settings::get_paid_content_settings();
$user = get_userdata($user_id);
if ($user && array_intersect((array) $user->roles, (array) ($paid['vip_exempt_roles'] ?? []))) {
$level = $vip2_enabled ? 2 : ($vip1_enabled ? 1 : 0);
return array_merge($empty, [
'level' => $level,
'state' => $level > 0 ? 'permanent' : 'unopened',
'source' => 'exempt',
'is_permanent' => $level > 0,
'current_level' => $level,
'current_state' => $level > 0 ? 'permanent' : 'unopened',
]);
}
if (class_exists('\AKSRT\Payment\Order')) {
$orders = \AKSRT\Payment\Order::get_instance();
$order = method_exists($orders, 'get_valid_vip_order') ? $orders->get_valid_vip_order($user_id) : null;
if (is_array($order) && $order !== []) {
$order_meta = is_array($order['meta_payload'] ?? null) ? $order['meta_payload'] : [];
if ($order_meta === [] && is_string($order['note'] ?? null)) {
$decoded = json_decode($order['note'], true);
$order_meta = is_array($decoded) ? $decoded : [];
}
$level = (int) ($order_meta['level'] ?? 0);
$product_id = (int) ($order['product_id'] ?? 0);
if ($level <= 0 && $product_id > 0) {
$level = $product_id >= 1000 ? (int) floor($product_id / 1000) : 1;
}
$level = min(2, max(1, $level));
$level = $level >= 2 && $vip2_enabled ? 2 : ($vip1_enabled ? 1 : 0);
$order_expires = max(0, (int) ($order_meta['expires'] ?? $order_meta['vip_expires'] ?? 0));
return array_merge($empty, [
'level' => $level,
'expires' => $order_expires,
'state' => $level > 0 ? 'active' : 'unopened',
'source' => 'order',
'current_level' => $level,
'current_expires' => $order_expires,
'current_state' => $level > 0 ? 'active' : 'unopened',
]);
}
}
return $empty;
}
/**
* @param int $user_id User ID, or current user when zero.
* @return int 0|1|2
*/
function aksrt_get_user_vip_level(int $user_id = 0): int {
return (int) aksrt_get_user_vip_state($user_id)['level'];
}
/**
* 检查用户是否为 VIP(level >= 1)。
*
* @param int $user_id
* @return bool
*/
function aksrt_is_vip(int $user_id = 0): bool {
return aksrt_get_user_vip_level($user_id) >= 1;
}
/**
* 检查用户是否为 VIP2(level >= 2)。
*
* @param int $user_id
* @return bool
*/
function aksrt_is_vip2(int $user_id = 0): bool {
return aksrt_get_user_vip_level($user_id) >= 2;
}
/**
* 获取 VIP 徽标文本
*
* @return string
*/
function aksrt_get_vip_badge_text(int $user_id = 0): string {
if (!class_exists('\AKSRT\Settings\Settings')) {
return 'VIP';
}
$vip_settings = \AKSRT\Settings\Settings::get_payment_vip_settings();
$level = $user_id > 0 ? aksrt_get_user_vip_level($user_id) : 0;
if ($level >= 1 && !empty($vip_settings['levels'][$level]['name'])) {
return sanitize_text_field((string) $vip_settings['levels'][$level]['name']);
}
return sanitize_text_field($vip_settings['badge_text'] ?? 'VIP');
}
/**
* Daily free-download limit for a user (0 = unlimited).
* Aligns with legacy pay_download_limit / pay_download_limit_vip_{n}.
*/
if (!function_exists('aksrt_get_user_free_download_limit')) {
function aksrt_get_user_free_download_limit(int $user_id = 0): int {
if ($user_id <= 0) {
$user_id = get_current_user_id();
}
if ($user_id <= 0 || !class_exists('\AKSRT\Settings\Settings')) {
return 0;
}
$vip = \AKSRT\Settings\Settings::get_payment_vip_settings();
$level = function_exists('aksrt_get_user_vip_level') ? aksrt_get_user_vip_level($user_id) : 0;
$levels = is_array($vip['levels'] ?? null) ? $vip['levels'] : [];
$benefit = is_array($vip['benefit'] ?? null) ? $vip['benefit'] : [];
if ($level >= 1 && !empty($levels[$level]['enabled'])) {
return max(0, (int) ($levels[$level]['download_limit'] ?? 0));
}
return max(0, (int) ($benefit['download_limit'] ?? 0));
}
}
/**
* Count free downloads used today for a user (legacy pay_down_number).
*/
if (!function_exists('aksrt_get_user_free_downloaded_number')) {
function aksrt_get_user_free_downloaded_number(int $user_id = 0): int {
if ($user_id <= 0) {
$user_id = get_current_user_id();
}
if ($user_id <= 0) {
return 0;
}
$day = current_time('Y-m-d');
$mate = get_user_meta($user_id, 'aksrt_pay_down_number', true);
if (!is_array($mate) || empty($mate[$day]) || !is_array($mate[$day])) {
return 0;
}
return count($mate[$day]);
}
}
/**
* Record a free download for daily quota (legacy pay_down_number).
*/
if (!function_exists('aksrt_record_user_free_download')) {
function aksrt_record_user_free_download(int $user_id, int $post_id): void {
if ($user_id <= 0 || $post_id <= 0) {
return;
}
$day = current_time('Y-m-d');
$mate = get_user_meta($user_id, 'aksrt_pay_down_number', true);
if (!is_array($mate)) {
$mate = [];
}
// Drop old days to keep meta small.
foreach (array_keys($mate) as $k) {
if ($k !== $day) {
unset($mate[$k]);
}
}
if (!isset($mate[$day]) || !is_array($mate[$day])) {
$mate[$day] = [];
}
$mate[$day][(string) $post_id] = 1;
update_user_meta($user_id, 'aksrt_pay_down_number', $mate);
}
}
/**
* Whether a free download is allowed under daily quota.
* Paid downloads (cost > 0) always pass.
*
* @return true|WP_Error
*/
if (!function_exists('aksrt_check_free_download_quota')) {
function aksrt_check_free_download_quota(int $user_id, int $cost = 0) {
if ($cost > 0) {
return true;
}
if ($user_id <= 0) {
return true; // login gate handled elsewhere
}
$limit = aksrt_get_user_free_download_limit($user_id);
if ($limit <= 0) {
return true; // unlimited
}
$used = aksrt_get_user_free_downloaded_number($user_id);
if ($used >= $limit) {
$data = [
'status' => 429,
'limit' => $limit,
'used' => $used,
];
// Optional overbuy price for free-quota exhausted (legacy download_limit_over_price).
// Callers may pass post context via filter.
$over = apply_filters('aksrt_download_limit_over_price', 0, $user_id);
if ($over > 0) {
$data['download_limit_over_price'] = $over;
$data['allow_limit_over'] = true;
}
return new WP_Error(
'aksrt_download_limit',
__('您今日下载免费资源个数已超限,请明日再下载。', 'aksrt'),
$data
);
}
return true;
}
}
/**
* Remaining free downloads today (null = unlimited).
*/
if (!function_exists('aksrt_get_free_download_surplus')) {
function aksrt_get_free_download_surplus(int $user_id = 0): ?int {
if ($user_id <= 0) {
$user_id = get_current_user_id();
}
$limit = aksrt_get_user_free_download_limit($user_id);
if ($limit <= 0) {
return null;
}
$used = aksrt_get_user_free_downloaded_number($user_id);
return max(0, $limit - $used);
}
}
/**
* Render the configured VIP icon with the frontend identity-image contract.
*
* @param int $user_id User ID.
* @param string $class Additional image classes.
* @param bool $tooltip Whether to enable the tooltip trigger.
*/
function aksrt_get_vip_icon(int $user_id = 0, string $class = 'em12 ml3', bool $tooltip = true): string {
$level = aksrt_get_user_vip_level($user_id);
if ($level <= 0 || !class_exists('\AKSRT\Settings\Settings')) {
return '';
}
$vip_settings = \AKSRT\Settings\Settings::get_payment_vip_settings();
$icon_url = esc_url((string) ($vip_settings['levels'][$level]['img_icon'] ?? ''));
if ($icon_url === '') {
return '';
}
$name = sanitize_text_field((string) ($vip_settings['levels'][$level]['name'] ?? aksrt_get_vip_badge_text($user_id)));
$tooltip_attr = $tooltip ? ' data-toggle="tooltip"' : '';
return '';
}
/**
* 获取用户 VIP 等级信息(用于展示)
*
* @param int $user_id
* @return array{is_vip: bool, level: int, badge_text: string, badge_html: string, name: string, expires: int, expires_label: string}
*/
function aksrt_get_vip_info(int $user_id = 0): array {
$state = function_exists('aksrt_get_user_vip_state') ? aksrt_get_user_vip_state($user_id) : [];
$level = (int) ($state['level'] ?? 0);
$is_vip = $level >= 1;
$badge_text = aksrt_get_vip_badge_text($user_id);
$expires = max(0, (int) ($state['expires'] ?? 0));
$expires_label = __('未开通', 'aksrt');
if (($state['state'] ?? '') === 'expired') {
$expires_label = __('已过期', 'aksrt');
} elseif ($is_vip && $expires <= 0 && ($state['source'] ?? 'none') !== 'order') {
$expires_label = __('永久有效', 'aksrt');
} elseif ($is_vip) {
$expires_label = sprintf(__('到期时间:%s', 'aksrt'), wp_date('Y-m-d H:i', $expires));
}
$name = __('普通用户', 'aksrt');
if ($is_vip && class_exists('\AKSRT\Settings\Settings')) {
$vip = \AKSRT\Settings\Settings::get_payment_vip_settings();
$name = (string) ($vip['levels'][$level]['name'] ?? ($badge_text . __('会员', 'aksrt')));
} elseif ($is_vip) {
$name = $badge_text . __('会员', 'aksrt');
}
return [
'is_vip' => $is_vip,
'level' => $level,
'badge_text' => $badge_text,
'badge_html' => $is_vip ? aksrt_get_vip_icon($user_id) : '',
'name' => $name,
'expires' => $expires,
'expires_label' => $expires_label,
'state' => (string) ($state['state'] ?? 'unopened'),
'source' => (string) ($state['source'] ?? 'none'),
];
}
/**
* VIP 开通页入口(用户中心 credits 面板)。
*/
if (!function_exists('aksrt_get_vip_purchase_url')) {
function aksrt_get_vip_purchase_url(): string {
if (function_exists('aksrt_get_user_center_tab_url')) {
return aksrt_get_user_center_tab_url('vip');
}
if (function_exists('aksrt_get_user_center_url')) {
return aksrt_get_user_center_url('vip');
}
return home_url('/');
}
}
/**
* 用户中心 /user/vip/ 页面的积分兑换会员入口(参考主题 zibpay_user_page_tab_content_vip 1:1)。
*
* 参考主题在「我的会员」摘要与两张档位购买卡之后输出全宽入口横条:
* .c-yellow.zib-widget.flex.ac.jsb(zib-widget → 本主题 aksrt-panel 表面)
* ├─ .flex1.em12:points-color 图标 +「积分兑换会员」
* └─ .badg.radius.c-yellow:「立即兑换」+ fa-chevron-circle-right
* 门控对齐参考主题 zibpay_get_vip_points_exchange_link:已是会员不展示
* ($vip_level ? '' : link),且要求积分兑换会员开启、至少一个会员等级
* 开启并存在可兑换套餐(catalog.points_exchange 已内含 credits 门控)。
*
* 入口点击经 data-aksrt-vip-points-open 由 pay-vip.js 打开 wp_footer 输出的
* 静态兑换弹窗(aksrt_render_vip_points_exchange_modal),与会员购买弹窗内
* 的入口共用同一弹窗与开合开关。
*
* @param int $user_id 当前用户 ID(<=0 返回空串)。
* @param int $vip_level 用户当前会员等级(>0 时返回空串;缺省 -1 表示自行解析)。
* @return string 空串表示不满足展示条件。
*/
if (!function_exists('aksrt_get_vip_points_exchange_entry_html')) :
function aksrt_get_vip_points_exchange_entry_html(int $user_id, int $vip_level = -1): string {
if ($user_id <= 0 || !class_exists('\AKSRT\Content\VipService')) {
return '';
}
if ($vip_level < 0) {
$vip_level = function_exists('aksrt_get_user_vip_level') ? aksrt_get_user_vip_level($user_id) : 0;
}
// 参考主题契约:已经是会员就不再展示积分兑换入口。
if ($vip_level > 0) {
return '';
}
$catalog = \AKSRT\Content\VipService::get_instance()->get_catalog($user_id);
$points_exchange = is_array($catalog['points_exchange'] ?? null) ? $catalog['points_exchange'] : [];
$packages = is_array($points_exchange['packages'] ?? null) ? $points_exchange['packages'] : [];
if (empty($points_exchange['enabled']) || $packages === []) {
return '';
}
return ''
. ''
. ''
. esc_html__('积分兑换会员', 'aksrt')
. ''
. '' . esc_html__('立即兑换', 'aksrt')
. '';
}
endif;
/**
* Format VIP equity HTML for front-end display.
*
* Admin field is free-form HTML (typically one or more