diff --git a/inc/helpers/aksrt-header-menu-buttons.php b/inc/helpers/aksrt-header-menu-buttons.php index 87686a16..4b4a907c 100644 --- a/inc/helpers/aksrt-header-menu-buttons.php +++ b/inc/helpers/aksrt-header-menu-buttons.php @@ -161,13 +161,12 @@ if (!function_exists('aksrt_render_header_menu_buttons')) : } } - // VIP open CTA for guests / non-VIP. - $user_id = get_current_user_id(); - $vip_url = function_exists('aksrt_get_vip_purchase_url') ? aksrt_get_vip_purchase_url() : (function_exists('aksrt_get_user_center_url') ? aksrt_get_user_center_url('vip') : '#'); - $show_vip = true; - if (class_exists('\AKSRT\Settings\Settings') && method_exists('\AKSRT\Settings\Settings', 'get_vip_settings')) { - $vs = \AKSRT\Settings\Settings::get_vip_settings(); - $show_vip = !empty($vs['enabled']) || !empty($vs['vip1_enabled']) || !empty($vs['vip2_enabled']); + // VIP open CTA for guests / non-VIP (zibll: .pay-vip modal). + $user_id = get_current_user_id(); + $show_vip = false; + if (class_exists('\AKSRT\Settings\Settings')) { + $vs = \AKSRT\Settings\Settings::get_payment_vip_settings(); + $show_vip = !empty($vs['enabled']); } if ($show_vip) { $is_vip = false; @@ -175,9 +174,9 @@ if (!function_exists('aksrt_render_header_menu_buttons')) : $is_vip = (int) aksrt_get_user_vip_level($user_id) > 0; } if (!$is_vip) { - $cls = $user_id ? 'pay-vip' : 'signin-loader'; + $cls = $user_id > 0 ? 'pay-vip' : 'signin-loader'; $html .= ''; } diff --git a/inc/helpers/aksrt-header-userbox.php b/inc/helpers/aksrt-header-userbox.php index 0f293fa9..c5db481b 100644 --- a/inc/helpers/aksrt-header-userbox.php +++ b/inc/helpers/aksrt-header-userbox.php @@ -85,7 +85,7 @@ if (!function_exists('aksrt_header_user_wallet_mini')) : if (class_exists('\AKSRT\Credits\Balance')) { $wallet = (float) \AKSRT\Credits\Balance::balance($user_id); } - $center = function_exists('aksrt_get_user_center_url') ? aksrt_get_user_center_url('assets') : '#'; + $center = function_exists('aksrt_get_user_center_url') ? aksrt_get_user_center_url('credits') : '#'; $html = ''; $html .= ' ' @@ -116,29 +116,44 @@ endif; if (!function_exists('aksrt_header_payvip_bar')) : /** - * VIP status / open CTA bar. + * VIP status / open CTA bar (zibll zib_get_header_payvip_icon parity). */ function aksrt_header_payvip_bar(int $user_id = 0): string { $user_id = $user_id > 0 ? $user_id : get_current_user_id(); if ($user_id <= 0) { return ''; } - $badge = function_exists('aksrt_render_vip_badge') ? aksrt_render_vip_badge($user_id, 'md') : ''; - if ($badge !== '') { - return '
' . $badge . '
'; - } - // Guest-style open VIP for non-members if VIP feature on. - $vip_url = function_exists('aksrt_get_user_center_url') ? aksrt_get_user_center_url('vip') : '#'; - $enabled = true; - if (class_exists('\AKSRT\Settings\Settings') && method_exists('\AKSRT\Settings\Settings', 'get_vip_settings')) { - $vs = \AKSRT\Settings\Settings::get_vip_settings(); - $enabled = !empty($vs['enabled']) || !empty($vs['vip1_enabled']) || !empty($vs['vip2_enabled']); - } - if (!$enabled) { + + $vip = class_exists('\AKSRT\Settings\Settings') + ? \AKSRT\Settings\Settings::get_payment_vip_settings() + : ['enabled' => false, 'levels' => []]; + if (empty($vip['enabled'])) { return ''; } + + $level = function_exists('aksrt_get_user_vip_level') ? (int) aksrt_get_user_vip_level($user_id) : 0; + if ($level > 0) { + $levels = is_array($vip['levels'] ?? null) ? $vip['levels'] : []; + $name = (string) ($levels[$level]['name'] ?? ($level === 2 ? __('二级会员', 'aksrt') : __('一级会员', 'aksrt'))); + $exp_label = ''; + if (function_exists('aksrt_get_vip_info')) { + $info = aksrt_get_vip_info($user_id); + $exp_label = (string) ($info['expires_label'] ?? ''); + } + $html = '
'; + $html .= ''; + $html .= ''; + $html .= '' . esc_html($name) . ''; + if ($exp_label !== '') { + $html .= '' + . esc_html($exp_label) . ''; + } + $html .= '
'; + return $html; + } + return '
' - . '' + . '' . '' . esc_html__('开通会员 尊享会员权益', 'aksrt') . '
'; diff --git a/inc/helpers/aksrt-runtime-win.php b/inc/helpers/aksrt-runtime-win.php index 354c2e0d..9771cd90 100644 --- a/inc/helpers/aksrt-runtime-win.php +++ b/inc/helpers/aksrt-runtime-win.php @@ -57,10 +57,10 @@ if (!function_exists('aksrt_get_float_pay_vip_html')) : * Floating VIP open button. */ function aksrt_get_float_pay_vip_html(array $item = []): string { - $show_vip = true; - if (class_exists('\AKSRT\Settings\Settings') && method_exists('\AKSRT\Settings\Settings', 'get_vip_settings')) { - $vs = \AKSRT\Settings\Settings::get_vip_settings(); - $show_vip = !empty($vs['enabled']) || !empty($vs['vip1_enabled']) || !empty($vs['vip2_enabled']); + $show_vip = false; + if (class_exists('\AKSRT\Settings\Settings')) { + $vs = \AKSRT\Settings\Settings::get_payment_vip_settings(); + $show_vip = !empty($vs['enabled']); } if (!$show_vip) { return ''; @@ -74,13 +74,11 @@ if (!function_exists('aksrt_get_float_pay_vip_html')) : if ($level >= 2) { return ''; } - $vip_url = function_exists('aksrt_get_vip_purchase_url') - ? aksrt_get_vip_purchase_url() - : (function_exists('aksrt_get_user_center_url') ? aksrt_get_user_center_url('vip') : '#'); $title = $level === 1 ? __('升级会员', 'aksrt') : __('开通会员', 'aksrt'); $icon = !empty($item['icon']) ? (string) $item['icon'] : 'fa fa-diamond'; $cls = $user_id > 0 ? 'float-btn pay-vip' : 'float-btn signin-loader'; - $href = $user_id > 0 ? $vip_url : 'javascript:;'; + // Modal path: .pay-vip opens VIP modal (Batch 2+); guests use signin-loader. + $href = 'javascript:;'; $style = []; if (!empty($item['color'])) { $style[] = '--this-color:' . esc_attr((string) $item['color']); @@ -89,7 +87,8 @@ if (!function_exists('aksrt_get_float_pay_vip_html')) : $style[] = '--this-bg:' . esc_attr((string) $item['bg']); } $style_attr = $style ? ' style="' . implode(';', $style) . '"' : ''; - return '' + $vip_level_attr = $user_id > 0 ? ' vip-level="' . esc_attr((string) max(1, min(2, $level + 1))) . '"' : ''; + return '' . '' . ''; } @@ -152,28 +151,3 @@ if (!function_exists('aksrt_get_win_runtime_config')) : ]; } endif; - -if (!function_exists('aksrt_print_win_var')) : - /** - * Echo window._win + alias window.aksrtData bridge. - */ - function aksrt_print_win_var(): void { - static $done = false; - if ($done) { - return; - } - $done = true; - $cfg = aksrt_get_win_runtime_config(); - // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped - echo '' . "\n"; - } -endif; diff --git a/inc/helpers/aksrt-user-center.php b/inc/helpers/aksrt-user-center.php index 911521af..3a1ab39e 100644 --- a/inc/helpers/aksrt-user-center.php +++ b/inc/helpers/aksrt-user-center.php @@ -296,10 +296,13 @@ if (!function_exists('aksrt_user_center_resolve_tabs')) { $keys = array_keys($tabs); $default = in_array('profile', $keys, true) ? 'profile' : ($keys[0] ?? 'profile'); $active = isset($_GET['tab']) ? sanitize_key(wp_unslash($_GET['tab'])) : $default; - if ('signin' === $active) { + if ('signin' === $active || 'assets' === $active) { $active = 'credits'; } elseif ('invite' === $active) { $active = 'settings'; + } elseif ('vip' === $active) { + // Prefer dedicated vip tab when registered; else credits (VIP shop). + $active = in_array('vip', $keys, true) ? 'vip' : 'credits'; } if (!in_array($active, $keys, true)) { $active = $default; @@ -353,7 +356,13 @@ if (!function_exists('aksrt_user_center_runtime_config')) { 'userId' => $user_id, 'defaultTab' => $default_tab, 'activeTab' => $active_tab, - 'tabAliases' => ['signin' => 'credits', 'invite' => 'settings'], + 'tabAliases' => [ + 'signin' => 'credits', + 'invite' => 'settings', + 'assets' => 'credits', + // vip → vip when panel exists; SPA may still map missing keys via server resolve. + 'vip' => 'vip', + ], 'tabUrls' => $tab_urls, 'i18n' => [ 'contactUrlPlaceholder' => __('链接 URL', 'aksrt'), diff --git a/inc/helpers/aksrt-vip.php b/inc/helpers/aksrt-vip.php index c8e9f927..61870490 100644 --- a/inc/helpers/aksrt-vip.php +++ b/inc/helpers/aksrt-vip.php @@ -65,7 +65,8 @@ function aksrt_get_user_vip_level(int $user_id = 0): int { if ($meta_level > 0) { if ($expires > 0 && $expires < time()) { - // 过期:lazy 清 level,与 capabilities 读 meta 对齐。 + // 过期:lazy 清 level,记录上次等级(zibll vip_level_expired)。 + update_user_meta($user_id, 'aksrt_vip_level_expired', min(2, max(1, $meta_level))); update_user_meta($user_id, 'aksrt_vip_level', 0); $candidate = 0; } else { @@ -82,16 +83,36 @@ function aksrt_get_user_vip_level(int $user_id = 0): int { : null; if (is_array($valid_order) && !empty($valid_order)) { - $order_expires = trim((string) ($valid_order['expires_at'] ?? '')); - $order_expired = false; - if ($order_expires !== '') { - $ts = strtotime($order_expires); - if ($ts !== false && $ts < time()) { - $order_expired = true; + // Order.expires_at is payment checkout deadline, not VIP membership expiry. + // Prefer note/meta_payload level + aksrt_vip_expires if present. + $meta = []; + if (!empty($valid_order['meta_payload']) && is_array($valid_order['meta_payload'])) { + $meta = $valid_order['meta_payload']; + } elseif (!empty($valid_order['note']) && is_string($valid_order['note'])) { + $decoded = json_decode($valid_order['note'], true); + if (is_array($decoded)) { + $meta = $decoded; } } - if (!$order_expired) { - // 套餐无 level 列:有效 VIP 订单至少视为 VIP1。 + + $order_level = (int) ($meta['level'] ?? 0); + if ($order_level <= 0 && !empty($valid_order['product_id']) && class_exists('\AKSRT\Content\VipService')) { + // package_id encodes level: decode if possible via service helpers. + $pkg_id = (int) $valid_order['product_id']; + // Encode scheme: level * 1000 + index (see VipServicePackages). + if ($pkg_id >= 1000) { + $order_level = (int) floor($pkg_id / 1000); + } elseif ($pkg_id > 0) { + $order_level = 1; + } + } + $order_level = max(0, min(2, $order_level)); + + // Membership still valid if user meta expires is in future or permanent (0 with prior grant). + // Without restored meta, treat paid VIP order as at least the ordered level. + if ($order_level > 0) { + $candidate = $order_level; + } else { $candidate = 1; } } diff --git a/inc/modules/user-parts/urls.php b/inc/modules/user-parts/urls.php index a4dbcbc3..e565c899 100644 --- a/inc/modules/user-parts/urls.php +++ b/inc/modules/user-parts/urls.php @@ -26,6 +26,17 @@ function aksrt_get_user_center_url(string $tab = ''): string { } if ($tab !== '') { + $tab = sanitize_key($tab); + // Deep-link aliases used by header wallet / VIP CTA. + $aliases = [ + 'assets' => 'credits', + 'vip' => 'vip', // dedicated vip tab when registered; resolver falls back if missing + 'signin' => 'credits', + 'invite' => 'settings', + ]; + if (isset($aliases[$tab])) { + $tab = $aliases[$tab]; + } $url = add_query_arg('tab', $tab, $url); } return $url; diff --git a/src/AKSRT/Payment/OrderRepositoryCore.php b/src/AKSRT/Payment/OrderRepositoryCore.php index e3c498d6..ebb37522 100644 --- a/src/AKSRT/Payment/OrderRepositoryCore.php +++ b/src/AKSRT/Payment/OrderRepositoryCore.php @@ -29,6 +29,18 @@ trait OrderRepositoryCore { $order_number = $data['order_number'] ?? $this->generate_order_number(); + // Preserve JSON pay meta (VIP/content/coupon). Only strip tags for plain text notes. + $note_raw = (string) ($data['note'] ?? ''); + $note_trim = trim($note_raw); + if ($note_trim !== '' && ($note_trim[0] === '{' || $note_trim[0] === '[')) { + $decoded = json_decode($note_trim, true); + $note = is_array($decoded) + ? (string) wp_json_encode($decoded, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) + : sanitize_textarea_field($note_raw); + } else { + $note = sanitize_textarea_field($note_raw); + } + $wpdb->insert($this->table(), [ 'order_number' => $order_number, 'user_id' => (int) ($data['user_id'] ?? 0), @@ -42,7 +54,7 @@ trait OrderRepositoryCore { 'created_at' => $data['created_at'] ?? current_time('mysql'), 'paid_at' => $data['paid_at'] ?? null, 'expires_at' => $data['expires_at'] ?? null, - 'note' => sanitize_textarea_field((string) ($data['note'] ?? '')), + 'note' => $note, ], ['%s', '%d', '%s', '%d', '%f', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s']); if ($wpdb->insert_id) { diff --git a/src/AKSRT/Settings/CreditsPaymentGroups.php b/src/AKSRT/Settings/CreditsPaymentGroups.php index e9213afa..009a34b2 100644 --- a/src/AKSRT/Settings/CreditsPaymentGroups.php +++ b/src/AKSRT/Settings/CreditsPaymentGroups.php @@ -159,6 +159,15 @@ trait CreditsPaymentGroups { return PaymentRuntime::get_payment_vip_settings(); } + /** + * Alias for callers that expect get_vip_settings() (header CTA, float buttons). + * + * @return array + */ + public static function get_vip_settings(): array { + return self::get_payment_vip_settings(); + } + /** * @return array{enabled:bool,share_ratio:int,settlement_threshold:int,cycle_days:int,note:string} */ diff --git a/src/AKSRT/Settings/PaymentRuntime.php b/src/AKSRT/Settings/PaymentRuntime.php index 78b2d41b..e90df501 100644 --- a/src/AKSRT/Settings/PaymentRuntime.php +++ b/src/AKSRT/Settings/PaymentRuntime.php @@ -192,7 +192,8 @@ class PaymentRuntime { 'upgrade' => [ 'enabled' => !isset($settings['vip_upgrade']) || !empty($settings['vip_upgrade']), 'desc' => sanitize_textarea_field((string) ($settings['vip_upgrade_desc'] ?? '')), - 'product' => sanitize_text_field((string) ($settings['vip_upgrade_product'] ?? '')), + // Multi-line upgrade product rules — must not collapse newlines. + 'product' => sanitize_textarea_field((string) ($settings['vip_upgrade_product'] ?? '')), ], 'points_exchange' => [ 'enabled' => !empty($settings['pay_vip_points_exchange_s']),