feat(vip): Batch 1 per-level packages and admin schema

This commit is contained in:
2026-07-21 16:32:06 +08:00
parent 69836154fd
commit a4562627d8
6 changed files with 300 additions and 47 deletions
+16 -6
View File
@@ -100,18 +100,28 @@ class SchemaPayment {
'vip' => [
'key' => 'vip',
'title' => 'VIP会员',
'version' => '1.0.0',
'version' => '1.1.0',
'option_key' => 'aksrt_settings',
'groups' => [
['key' => 'basic', 'title' => '会员等级', 'fields' => [
['key' => 'pay_user_vip_1_s', 'label' => '启用一级会员', 'type' => 'switch', 'default' => true],
['key' => 'pay_user_vip_1_name', 'label' => '一级会员名称', 'type' => 'input', 'default' => '一级会员', 'dependsOn' => ['key' => 'pay_user_vip_1_s', 'value' => true]],
['key' => 'pay_user_vip_1_desc', 'label' => '一级会员一句话简介', 'type' => 'input', 'default' => '尊享一级会员特权', 'dependsOn' => ['key' => 'pay_user_vip_1_s', 'value' => true]],
['key' => 'pay_user_vip_1_equity', 'label' => '一级会员权益 HTML', 'type' => 'textarea', 'default' => '<li>免费下载部分资源</li><li>会员专属身份标识</li><li>付费内容会员价</li>', 'dependsOn' => ['key' => 'pay_user_vip_1_s', 'value' => true]],
['key' => 'vip_1_img_icon', 'label' => '一级会员图标 URL', 'type' => 'input', 'default' => '', 'dependsOn' => ['key' => 'pay_user_vip_1_s', 'value' => true]],
['key' => 'pay_user_vip_2_s', 'label' => '启用二级会员', 'type' => 'switch', 'default' => true],
['key' => 'pay_user_vip_2_name', 'label' => '二级会员名称', 'type' => 'input', 'default' => '二级会员', 'dependsOn' => ['key' => 'pay_user_vip_2_s', 'value' => true]],
['key' => 'pay_user_vip_desc', 'label' => '会员说明', 'type' => 'textarea', 'default' => ''],
['key' => 'pay_user_vip_2_desc', 'label' => '二级会员一句话简介', 'type' => 'input', 'default' => '尊享二级会员特权', 'dependsOn' => ['key' => 'pay_user_vip_2_s', 'value' => true]],
['key' => 'pay_user_vip_2_equity', 'label' => '二级会员权益 HTML', 'type' => 'textarea', 'default' => '<li>更高免费下载额度</li><li>更高等级身份标识</li><li>更多付费内容会员价</li>', 'dependsOn' => ['key' => 'pay_user_vip_2_s', 'value' => true]],
['key' => 'vip_2_img_icon', 'label' => '二级会员图标 URL', 'type' => 'input', 'default' => '', 'dependsOn' => ['key' => 'pay_user_vip_2_s', 'value' => true]],
['key' => 'pay_user_vip_desc', 'label' => '弹窗顶部会员说明', 'type' => 'textarea', 'default' => ''],
['key' => 'pay_user_vip_more', 'label' => '弹窗底部补充说明 HTML', 'type' => 'textarea', 'default' => ''],
]],
['key' => 'packages', 'title' => '会员套餐', 'fields' => [
['key' => 'payment_vip_packages', 'label' => '套餐列表', 'type' => 'textarea', 'default' => "30|30|月卡\n365|365|年卡", 'help' => '每行:现金价|天数|标题(天数为 0 表示永久)'],
['key' => 'packages', 'title' => '分档套餐', 'fields' => [
['key' => 'vip_1_product_s', 'label' => '允许购买一级会员', 'type' => 'switch', 'default' => true, 'dependsOn' => ['key' => 'pay_user_vip_1_s', 'value' => true]],
['key' => 'payment_vip_1_products', 'label' => '一级会员套餐', 'type' => 'textarea', 'default' => "30|59|1|month|限时|月卡\n98|199|3|month|特惠|季卡\n365|599|12|month|超值|年卡\n999|1299|0|day|永久|永久卡", 'dependsOn' => ['key' => 'vip_1_product_s', 'value' => true], 'help' => '每行:执行价|原价|时长|单位(day|month)|标签|标题;时长 0=永久'],
['key' => 'vip_2_product_s', 'label' => '允许购买二级会员', 'type' => 'switch', 'default' => true, 'dependsOn' => ['key' => 'pay_user_vip_2_s', 'value' => true]],
['key' => 'payment_vip_2_products', 'label' => '二级会员套餐', 'type' => 'textarea', 'default' => "58|99|1|month|限时|月卡\n168|299|3|month|特惠|季卡\n598|999|12|month|超值|年卡\n1299|1999|0|day|永久|永久卡", 'dependsOn' => ['key' => 'vip_2_product_s', 'value' => true], 'help' => '每行:执行价|原价|时长|单位(day|month)|标签|标题;时长 0=永久'],
]],
['key' => 'points_exchange', 'title' => '积分兑换会员', 'fields' => [
['key' => 'pay_vip_points_exchange_s', 'label' => '启用积分兑换', 'type' => 'switch', 'default' => false],
@@ -137,13 +147,13 @@ class SchemaPayment {
['label' => '折扣价格', 'value' => 'discount'],
['label' => '优惠立减', 'value' => 'reduce'],
]],
['key' => 'vip_renew_discount', 'label' => '续费折扣(%)', 'type' => 'number', 'default' => 8, 'min' => 1, 'max' => 100, 'dependsOn' => ['key' => 'vip_renew_price_type', 'value' => 'discount']],
['key' => 'vip_renew_discount', 'label' => '续费折扣(折,如 8=八折)', 'type' => 'number', 'default' => 8, 'min' => 1, 'max' => 100, 'dependsOn' => ['key' => 'vip_renew_price_type', 'value' => 'discount']],
['key' => 'vip_renew_reduce', 'label' => '续费减免金额', 'type' => 'number', 'default' => 20, 'min' => 0, 'max' => 9999, 'dependsOn' => ['key' => 'vip_renew_price_type', 'value' => 'reduce']],
]],
['key' => 'upgrade', 'title' => '升级设置', 'fields' => [
['key' => 'vip_upgrade', 'label' => '允许升级', 'type' => 'switch', 'default' => true],
['key' => 'vip_upgrade_desc', 'label' => '升级说明', 'type' => 'textarea', 'default' => '', 'dependsOn' => ['key' => 'vip_upgrade', 'value' => true]],
['key' => 'vip_upgrade_product', 'label' => '升级产品', 'type' => 'textarea', 'default' => '', 'dependsOn' => ['key' => 'vip_upgrade', 'value' => true], 'help' => '每行一条。永久VIP1:permanent|价格|标题;剩余天:unit|每日单价|标题;跳转永久:jump|价格|标题;兼容:价格|天数|标题'],
['key' => 'vip_upgrade_product', 'label' => '升级产品', 'type' => 'textarea', 'default' => "unit|1|按剩余天数升级\njump|199|升级永久二级会员\npermanent|299|永久一级升永久二级", 'dependsOn' => ['key' => 'vip_upgrade', 'value' => true], 'help' => '每行一条。永久VIP1:permanent|价格|标题;剩余天:unit|每日单价|标题;跳转永久:jump|价格|标题'],
]],
],
],
+85 -12
View File
@@ -12,11 +12,14 @@ trait VipServicePackages {
/**
* Parse packages_raw lines: price|days|title
* Parse per-level product lines (zibll vip_product).
*
* @return array<int, array{price:float,days:int,title:string}>
* Preferred: price|show_price|time|unit|tag|title
* Legacy: price|days|title
*
* @return array<int, array{price:float,show_price:float,time:int,unit:string,tag:string,tag_class:string,title:string,days:int}>
*/
public function parse_packages(string $raw): array {
public function parse_level_products(string $raw): array {
$lines = preg_split('/\r\n|\r|\n/', $raw) ?: [];
$out = [];
foreach ($lines as $line) {
@@ -26,20 +29,90 @@ trait VipServicePackages {
}
$parts = array_map('trim', explode('|', $line));
$price = isset($parts[0]) ? (float) $parts[0] : 0.0;
$days = isset($parts[1]) ? (int) $parts[1] : 0;
$title = isset($parts[2]) ? sanitize_text_field($parts[2]) : '';
if ($price <= 0) {
continue;
}
if ($title === '') {
$title = $days > 0
? sprintf(__('%d天会员', 'aksrt'), $days)
: __('永久会员', 'aksrt');
$n = count($parts);
// New format: price|show_price|time|unit|tag|title (6+)
// Or: price|show_price|time|unit|title (5)
if ($n >= 4 && (in_array(strtolower((string) ($parts[3] ?? '')), ['day', 'month', 'd', 'm'], true)
|| (isset($parts[3]) && $parts[3] !== '' && !is_numeric($parts[3])))) {
$show_price = isset($parts[1]) ? (float) $parts[1] : 0.0;
$time = isset($parts[2]) ? (int) $parts[2] : 0;
$unit_raw = strtolower((string) ($parts[3] ?? 'month'));
$unit = ($unit_raw === 'day' || $unit_raw === 'd') ? 'day' : 'month';
$tag = '';
$title = '';
if ($n >= 6) {
$tag = sanitize_text_field((string) ($parts[4] ?? ''));
$title = sanitize_text_field((string) ($parts[5] ?? ''));
} elseif ($n === 5) {
// price|show|time|unit|title
$title = sanitize_text_field((string) ($parts[4] ?? ''));
}
} else {
// Legacy: price|days|title
$show_price = 0.0;
$days_legacy = isset($parts[1]) ? (int) $parts[1] : 0;
$time = $days_legacy;
$unit = 'day';
$tag = '';
$title = isset($parts[2]) ? sanitize_text_field($parts[2]) : '';
}
$time = max(0, $time);
$days = $this->duration_to_days($time, $unit);
if ($title === '') {
if ($time <= 0) {
$title = __('永久会员', 'aksrt');
} elseif ($unit === 'day') {
$title = sprintf(__('%d天会员', 'aksrt'), $time);
} else {
$title = sprintf(__('%d个月会员', 'aksrt'), $time);
}
}
$out[] = [
'price' => round($price, 2),
'days' => max(0, $days),
'title' => $title,
'price' => round($price, 2),
'show_price' => max(0.0, round($show_price, 2)),
'time' => $time,
'unit' => $unit,
'tag' => $tag,
'tag_class' => '',
'title' => $title,
'days' => $days,
];
}
return $out;
}
/**
* Approximate days for catalog labels / order meta; activate prefers time+unit calendar math.
*/
public function duration_to_days(int $time, string $unit = 'day'): int {
if ($time <= 0) {
return 0;
}
if ($unit === 'month') {
return $time * 30;
}
return $time;
}
/**
* @deprecated Use parse_level_products — kept as thin adapter for upgrade fixed lines.
*
* @return array<int, array{price:float,days:int,title:string}>
*/
public function parse_packages(string $raw): array {
$parsed = $this->parse_level_products($raw);
$out = [];
foreach ($parsed as $p) {
$out[] = [
'price' => (float) $p['price'],
'days' => (int) $p['days'],
'title' => (string) $p['title'],
];
}
return $out;
+92 -11
View File
@@ -17,12 +17,11 @@ trait VipServicePurchase {
public function get_catalog(int $user_id = 0): array {
$vip = class_exists('\AKSRT\Settings\Settings')
? \AKSRT\Settings\Settings::get_payment_vip_settings()
: ['enabled' => false, 'packages_raw' => '', 'levels' => [], 'renew' => [], 'upgrade' => [], 'points_exchange' => []];
: ['enabled' => false, 'levels' => [], 'renew' => [], 'upgrade' => [], 'points_exchange' => []];
$level = function_exists('aksrt_get_user_vip_level') ? aksrt_get_user_vip_level($user_id) : 0;
$expires = $user_id > 0 ? (int) get_user_meta($user_id, 'aksrt_vip_expires', true) : 0;
$is_permanent = ($level > 0 && $expires <= 0);
$packages = $this->parse_packages((string) ($vip['packages_raw'] ?? ''));
$levels = is_array($vip['levels'] ?? null) ? $vip['levels'] : [];
$renew = is_array($vip['renew'] ?? null) ? $vip['renew'] : [];
$upgrade = is_array($vip['upgrade'] ?? null) ? $vip['upgrade'] : [];
@@ -37,6 +36,8 @@ trait VipServicePurchase {
if (empty($levels[$lv]['enabled'])) {
continue;
}
// Cash products can be disabled per level.
$product_s = !isset($levels[$lv]['product_s']) || !empty($levels[$lv]['product_s']);
if ($is_permanent && $lv <= $level) {
continue;
}
@@ -49,6 +50,9 @@ trait VipServicePurchase {
$name = (string) ($levels[$lv]['name'] ?? ('VIP' . $lv));
$items = [];
$packages = $product_s
? $this->parse_level_products((string) ($levels[$lv]['products_raw'] ?? ''))
: [];
foreach ($packages as $idx => $pkg) {
$base_price = (float) $pkg['price'];
$days = (int) $pkg['days'];
@@ -64,6 +68,22 @@ trait VipServicePurchase {
continue;
}
$show_price = (float) ($priced['show_price'] ?? 0);
if ($show_price <= 0 && !empty($pkg['show_price']) && $action === 'pay') {
$show_price = (float) $pkg['show_price'];
}
$discount_tag = (string) ($priced['tag'] ?? '');
if ($discount_tag === '' && $action === 'pay' && !empty($pkg['tag'])) {
$discount_tag = (string) $pkg['tag'];
}
$days_label = __('永久', 'aksrt');
if ((int) $pkg['time'] > 0) {
$days_label = ((string) ($pkg['unit'] ?? 'day') === 'month')
? sprintf(__('%d 个月', 'aksrt'), (int) $pkg['time'])
: sprintf(__('%d 天', 'aksrt'), (int) $pkg['time']);
}
$items[] = [
'package_id' => $this->encode_package_id($lv, $idx + 1),
'level' => $lv,
@@ -71,17 +91,17 @@ trait VipServicePurchase {
'action' => $action,
'price' => (float) $priced['price'],
'base_price' => $base_price,
'show_price' => (float) ($priced['show_price'] ?? 0),
'discount_tag' => (string) ($priced['tag'] ?? ''),
'show_price' => $show_price,
'discount_tag' => $discount_tag,
'days' => $days,
'time' => (int) ($pkg['time'] ?? 0),
'unit' => (string) ($pkg['unit'] ?? 'day'),
'title' => (string) $pkg['title'],
'price_label' => $this->format_cash_label((float) $priced['price']),
'show_price_label' => !empty($priced['show_price'])
? $this->format_cash_label((float) $priced['show_price'])
'show_price_label' => $show_price > 0
? $this->format_cash_label($show_price)
: '',
'days_label' => ($days <= 0)
? __('永久', 'aksrt')
: sprintf(__('%d 天', 'aksrt'), $days),
'days_label' => $days_label,
'cta_label' => $action === 'renew'
? __('立即续费', 'aksrt')
: ($action === 'upgrade' ? __('立即升级', 'aksrt') : __('立即开通', 'aksrt')),
@@ -163,7 +183,7 @@ trait VipServicePurchase {
$vip = class_exists('\AKSRT\Settings\Settings')
? \AKSRT\Settings\Settings::get_payment_vip_settings()
: ['enabled' => false, 'packages_raw' => '', 'levels' => [], 'renew' => [], 'upgrade' => []];
: ['enabled' => false, 'levels' => [], 'renew' => [], 'upgrade' => []];
if (empty($vip['enabled'])) {
return new \WP_Error('aksrt_vip_disabled', __('会员功能未启用', 'aksrt'), ['status' => 400]);
}
@@ -203,6 +223,8 @@ trait VipServicePurchase {
// Dedicated upgrade product:
// 901 permanent/fixed, 902 unit remaining days, 903 jump permanent.
$upgrade_mode = '';
$pkg_time = 0;
$pkg_unit = 'day';
if (in_array($index, [901, 902, 903], true)) {
$upgrade_items = $this->build_upgrade_items($user_id, $expires, $is_permanent, (string) ($upgrade['product'] ?? ''));
$match = null;
@@ -222,18 +244,30 @@ trait VipServicePurchase {
];
$amount = round((float) $match['price'], 2);
$days = (int) $match['days'];
$pkg_time = $days; // unit upgrade keeps days as remaining; jump/permanent use 0
$pkg_unit = 'day';
$base_price = (float) ($match['base_price'] ?? $amount);
$discount_tag = (string) ($match['discount_tag'] ?? __('升级专享', 'aksrt'));
$upgrade_mode = sanitize_key((string) ($match['upgrade_mode'] ?? ''));
$action = 'upgrade';
if ($upgrade_mode === 'jump' || $upgrade_mode === 'permanent') {
$pkg_time = 0;
$days = 0;
}
} else {
$packages = $this->parse_packages((string) ($vip['packages_raw'] ?? ''));
$level_cfg = is_array($levels[$level] ?? null) ? $levels[$level] : [];
if (isset($level_cfg['product_s']) && empty($level_cfg['product_s'])) {
return new \WP_Error('aksrt_vip_product_disabled', __('该等级暂不支持购买', 'aksrt'), ['status' => 400]);
}
$packages = $this->parse_level_products((string) ($level_cfg['products_raw'] ?? ''));
$pkg = $packages[$index - 1] ?? null;
if (!$pkg) {
return new \WP_Error('aksrt_invalid_package', __('套餐不存在', 'aksrt'), ['status' => 400]);
}
$base_price = (float) $pkg['price'];
$days = (int) $pkg['days'];
$pkg_time = (int) ($pkg['time'] ?? 0);
$pkg_unit = (string) ($pkg['unit'] ?? 'day');
$priced = $this->apply_action_price($base_price, $action, $renew, $upgrade, $level, $user_level, $expires, $is_permanent, $days);
if ($priced === null) {
return new \WP_Error('aksrt_invalid_package', __('当前无法购买该套餐', 'aksrt'), ['status' => 400]);
@@ -259,6 +293,8 @@ trait VipServicePurchase {
'vip_pay' => 1,
'level' => $level,
'days' => $days,
'time' => $pkg_time,
'unit' => $pkg_unit,
'package_index' => $index,
'package_title' => (string) ($pkg['title'] ?? ''),
'package_id' => $package_id,
@@ -401,6 +437,11 @@ trait VipServicePurchase {
$level = (int) ($meta['level'] ?? 0);
$days = (int) ($meta['days'] ?? 0);
$time = (int) ($meta['time'] ?? 0);
$unit = sanitize_key((string) ($meta['unit'] ?? 'day'));
if (!in_array($unit, ['day', 'month'], true)) {
$unit = 'day';
}
$upgrade_mode = sanitize_key((string) ($meta['upgrade_mode'] ?? ''));
if ($level <= 0) {
$decoded = $this->decode_package_id((int) ($paid_order['product_id'] ?? 0));
@@ -418,6 +459,9 @@ trait VipServicePurchase {
// unit upgrade: raise level only, keep current expiry (zibll).
if ($upgrade_mode === 'unit') {
$this->activate_vip($user_id, $level, 0, true);
} elseif ($time > 0 || (isset($meta['time']) && (int) $meta['time'] === 0 && $days === 0)) {
// Prefer calendar time+unit when present (including permanent time=0).
$this->activate_vip_duration($user_id, $level, $time, $unit);
} else {
$this->activate_vip($user_id, $level, $days, false);
}
@@ -439,6 +483,8 @@ trait VipServicePurchase {
$current_level = (int) get_user_meta($user_id, 'aksrt_vip_level', true);
$new_level = max($current_level, $level);
update_user_meta($user_id, 'aksrt_vip_level', $new_level);
// Clear expired memory when re-granting.
delete_user_meta($user_id, 'aksrt_vip_level_expired');
// Unit upgrade (zibll): keep vip_exp_date, only raise level.
if ($keep_expiry) {
@@ -460,6 +506,41 @@ trait VipServicePurchase {
update_user_meta($user_id, 'aksrt_vip_expires', $base + ($days * DAY_IN_SECONDS));
}
/**
* Activate VIP using calendar duration (zibll month/day).
* time=0 → permanent. Extends from max(now, current_exp).
*/
public function activate_vip_duration(int $user_id, int $level, int $time, string $unit = 'day'): void {
if ($user_id <= 0) {
return;
}
$level = max(1, min(2, $level));
$unit = $unit === 'month' ? 'month' : 'day';
$current_level = (int) get_user_meta($user_id, 'aksrt_vip_level', true);
$new_level = max($current_level, $level);
update_user_meta($user_id, 'aksrt_vip_level', $new_level);
delete_user_meta($user_id, 'aksrt_vip_level_expired');
if ($time <= 0) {
update_user_meta($user_id, 'aksrt_vip_expires', 0);
return;
}
$current_exp = (int) get_user_meta($user_id, 'aksrt_vip_expires', true);
if ($current_exp === 0 && $current_level > 0) {
return; // keep permanent
}
$base = max($current_exp, time());
$modifier = $unit === 'month'
? ('+' . $time . ' months')
: ('+' . $time . ' days');
$new_exp = strtotime($modifier, $base);
if ($new_exp === false) {
$new_exp = $base + ($this->duration_to_days($time, $unit) * DAY_IN_SECONDS);
}
update_user_meta($user_id, 'aksrt_vip_expires', (int) $new_exp);
}
/**
+3 -1
View File
@@ -153,7 +153,9 @@ trait CreditsPaymentGroups {
}
/**
* @return array{enabled:bool,name:string,badge_text:string,packages_raw:string,package_count:int,help_text:string}
* Dual-level VIP settings (Zibll pay_user_vip_1 / pay_user_vip_2).
*
* @return array<string,mixed>
*/
public static function get_payment_vip_settings(): array {
return PaymentRuntime::get_payment_vip_settings();
+41 -8
View File
@@ -150,27 +150,60 @@ class PaymentRuntime {
$renew_price_type = 'discount';
}
$packages = (string) ($settings['payment_vip_packages'] ?? "30|30|月卡\n365|365|年卡");
$default_l1 = "30|59|1|month|限时|月卡\n98|199|3|month|特惠|季卡\n365|599|12|month|超值|年卡\n999|1299|0|day|永久|永久卡";
$default_l2 = "58|99|1|month|限时|月卡\n168|299|3|month|特惠|季卡\n598|999|12|month|超值|年卡\n1299|1999|0|day|永久|永久卡";
$products_1 = sanitize_textarea_field((string) ($settings['payment_vip_1_products'] ?? $default_l1));
$products_2 = sanitize_textarea_field((string) ($settings['payment_vip_2_products'] ?? $default_l2));
// Restore newlines lost only if empty after sanitize — sanitize_textarea_field keeps \n.
if (trim($products_1) === '') {
$products_1 = $default_l1;
}
if (trim($products_2) === '') {
$products_2 = $default_l2;
}
$desc = sanitize_textarea_field((string) ($settings['pay_user_vip_desc'] ?? ''));
$more = (string) ($settings['pay_user_vip_more'] ?? '');
$allowed_equity = [
'li' => [],
'ul' => [],
'ol' => [],
'strong' => [],
'b' => [],
'em' => [],
'i' => ['class' => true],
'span' => ['class' => true],
'br' => [],
'a' => ['href' => true, 'target' => true, 'rel' => true, 'class' => true],
];
return [
'enabled' => $enabled,
'name' => $vip1_enabled ? $name1 : $badge,
'badge_text' => $badge,
'desc' => $desc,
'packages_raw' => $packages,
'package_count' => self::count_structured_lines($packages),
'more' => wp_kses($more, $allowed_equity + ['div' => ['class' => true], 'p' => ['class' => true]]),
'help_text' => $desc !== '' ? $desc : __('会员权益以免扣下载和身份展示为主,开通后立即生效。', 'aksrt'),
'levels' => [
1 => [
'enabled' => $vip1_enabled,
'name' => $name1,
'enabled' => $vip1_enabled,
'name' => $name1,
'desc' => sanitize_text_field((string) ($settings['pay_user_vip_1_desc'] ?? '')),
'equity' => wp_kses((string) ($settings['pay_user_vip_1_equity'] ?? ''), $allowed_equity),
'img_icon' => esc_url_raw((string) ($settings['vip_1_img_icon'] ?? '')),
'product_s' => !isset($settings['vip_1_product_s']) || !empty($settings['vip_1_product_s']),
'products_raw' => $products_1,
'download_limit' => max(0, min(9999, (int) ($settings['pay_download_limit_vip_1'] ?? 0))),
'download_speed' => max(0, min(99999, (int) ($settings['pay_download_speed_vip_1'] ?? 0))),
],
2 => [
'enabled' => $vip2_enabled,
'name' => $name2,
'enabled' => $vip2_enabled,
'name' => $name2,
'desc' => sanitize_text_field((string) ($settings['pay_user_vip_2_desc'] ?? '')),
'equity' => wp_kses((string) ($settings['pay_user_vip_2_equity'] ?? ''), $allowed_equity),
'img_icon' => esc_url_raw((string) ($settings['vip_2_img_icon'] ?? '')),
'product_s' => !isset($settings['vip_2_product_s']) || !empty($settings['vip_2_product_s']),
'products_raw' => $products_2,
'download_limit' => max(0, min(9999, (int) ($settings['pay_download_limit_vip_2'] ?? 0))),
'download_speed' => max(0, min(99999, (int) ($settings['pay_download_speed_vip_2'] ?? 0))),
],
@@ -198,7 +231,7 @@ class PaymentRuntime {
'points_exchange' => [
'enabled' => !empty($settings['pay_vip_points_exchange_s']),
'desc' => sanitize_textarea_field((string) ($settings['pay_vip_points_exchange_desc'] ?? '')),
'packages_raw' => (string) ($settings['payment_vip_points_packages'] ?? "1|500|30|积分月卡\n1|5000|0|积分永久"),
'packages_raw' => sanitize_textarea_field((string) ($settings['payment_vip_points_packages'] ?? "1|500|30|积分月卡\n1|5000|0|积分永久")),
],
];
}