fix(vip): Batch 0 wiring for VIP settings, URLs, and order meta

Wire get_vip_settings alias, tab aliases for vip/assets, expired-level meta,
VIP2 order-level fallback, JSON-safe order notes, and header/float pay-vip CTAs.
This commit is contained in:
2026-07-21 16:16:43 +08:00
parent 01719b7fa1
commit 69836154fd
9 changed files with 122 additions and 71 deletions
+8 -9
View File
@@ -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 .= '<div class="navbar-form navbar-right navbar-vip hover-show inline-block">';
$html .= '<a class="but jb-red radius payvip-icon ml10 ' . esc_attr($cls) . '" href="' . ($user_id ? esc_url($vip_url) : 'javascript:;') . '">';
$html .= '<a class="but jb-red radius payvip-icon ml10 ' . esc_attr($cls) . '" href="javascript:;"' . ($user_id > 0 ? ' vip-level="1"' : '') . '>';
$html .= '<i class="fa fa-diamond em12 mr3" aria-hidden="true"></i>' . esc_html__('开通会员', 'aksrt');
$html .= '</a></div>';
}
+30 -15
View File
@@ -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 = '<a class="aksrt-header-wallet flex ac jsb" href="' . esc_url($center) . '">';
$html .= '<span class="aksrt-header-wallet__item"><i class="fas fa-coins" aria-hidden="true"></i> '
@@ -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 '<div class="mt10 em09 aksrt-header-payvip">' . $badge . '</div>';
}
// 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 = '<div class="mt10 em09 aksrt-header-payvip">';
$html .= '<span class="radius4 payvip-icon btn-block text-center vipbg-v2 level-' . (int) $level . '">';
$html .= '<i class="fa fa-diamond em12 mr6" aria-hidden="true"></i>';
$html .= '<span>' . esc_html($name) . '</span>';
if ($exp_label !== '') {
$html .= '<span class="ml10 badg jb-yellow vip-expdate-tag"><i class="fa fa-clock-o mr3" aria-hidden="true"></i>'
. esc_html($exp_label) . '</span>';
}
$html .= '</span></div>';
return $html;
}
return '<div class="mt10 em09 aksrt-header-payvip">'
. '<a class="but jb-red radius4 payvip-icon btn-block" href="' . esc_url($vip_url) . '">'
. '<a class="pay-vip but radius4 payvip-icon btn-block" href="javascript:;" vip-level="1">'
. '<i class="fa fa-diamond mr6" aria-hidden="true"></i>'
. esc_html__('开通会员 尊享会员权益', 'aksrt')
. '</a></div>';
+8 -34
View File
@@ -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 '<a class="' . esc_attr($cls) . '" href="' . esc_url($href) . '" title="' . esc_attr($title) . '"' . $style_attr . '>'
$vip_level_attr = $user_id > 0 ? ' vip-level="' . esc_attr((string) max(1, min(2, $level + 1))) . '"' : '';
return '<a class="' . esc_attr($cls) . '" href="' . esc_attr($href) . '" title="' . esc_attr($title) . '"' . $style_attr . $vip_level_attr . '>'
. '<i class="' . esc_attr($icon) . '" aria-hidden="true"></i>'
. '</a>';
}
@@ -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 '<script type="text/javascript">window._win=' . wp_json_encode($cfg, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . ';'
. 'window.aksrtData=window.aksrtData||{};'
. 'window.aksrtData.ajaxUrl=window.aksrtData.ajaxUrl||window._win.ajax_url;'
. 'window.aksrtData.siteUrl=window.aksrtData.siteUrl||window._win.www;'
. 'window.aksrtData.themeUri=window.aksrtData.themeUri||window._win.uri;'
. 'window.aksrtData.restUrl=window.aksrtData.restUrl||window._win.rest_url;'
. 'window.aksrtData.restNonce=window.aksrtData.restNonce||window._win.rest_nonce;'
. 'window.aksrtData.nonce=window.aksrtData.nonce||window._win.nonce;'
. 'window.aksrtData.imgbox=window._win;'
. '</script>' . "\n";
}
endif;
+11 -2
View File
@@ -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'),
+30 -9
View File
@@ -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;
}
}
+11
View File
@@ -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;
+13 -1
View File
@@ -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) {
@@ -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<string,mixed>
*/
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}
*/
+2 -1
View File
@@ -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']),