feat(pay): wire VIP-tier pricing into cost, gates, and download box

Resolve user-facing pay amounts via aksrt_get_user_pay_price (cash/points +
VIP1/2 tiers), surface pay_modo/pay_limit through download meta and REST policy,
and tighten content-visibility / download-box UX for member-only and purchase
gates while improving local stream throttle math.
This commit is contained in:
kate522 committed 2026-07-14 11:13:17 +08:00
1 parent 1c8e4d3919
commit abe2f57417
5 files changed
+459 -127

No files matched your search

+87 -22
View File
@@ -38,7 +38,17 @@ class AKSRT_Content_Visibility {
? 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));
$price = (float) ($pay_meta['price'] ?? 0);
$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()
: [
@@ -50,6 +60,7 @@ class AKSRT_Content_Visibility {
$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,
@@ -59,6 +70,8 @@ class AKSRT_Content_Visibility {
'can_view' => true,
'can_download' => true,
'download_cost' => 0,
'pay_limit' => 0,
'user_vip_level' => 0,
];
if (!($resource_post instanceof WP_Post)) {
@@ -68,20 +81,16 @@ class AKSRT_Content_Visibility {
}
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;
// Prefer points_price when pay_modo=points.
if (is_array($pay_meta) && (($pay_meta['pay_modo'] ?? '0') === 'points')) {
$price = (float) ($pay_meta['points_price'] ?? $price);
$state['price'] = $price;
$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')) {
@@ -93,47 +102,60 @@ class AKSRT_Content_Visibility {
}
$has_vip_access = false;
$user_vip_level = 0;
$user_vip_level = is_array($priced) ? (int) ($priced['vip_level'] ?? 0) : 0;
if ($uid > 0) {
if (function_exists('aksrt_get_user_vip_level')) {
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 (function_exists('aksrt_is_vip') && ($pay_type === AKSRT_Domain_Model::PAY_TYPE_VIP || $pay_limit > 0)) {
} 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;
$state['pay_limit'] = $pay_limit;
$allowed_member = $has_vip_access && $user_vip_level >= $pay_limit;
$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 || $has_vip_access);
$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 || $has_vip_access);
$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 || $has_paid_order;
$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;
$state['need_purchase'] = !$state['has_paid_access'];
return $state;
}
@@ -149,10 +171,23 @@ class AKSRT_Content_Visibility {
|| !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']);
$state['need_purchase'] = !$has_vip_access && $state['download_cost'] > 0;
$state['has_paid_access'] = $has_vip_access || $has_paid_order || !$state['need_purchase'];
// 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;
$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;
}
@@ -167,8 +202,13 @@ class AKSRT_Content_Visibility {
}
$state['need_login'] = !empty($pay_meta['require_login']);
$state['need_purchase'] = !$has_free_access;
$state['has_paid_access'] = $has_free_access || $has_paid_order || $has_vip_access;
$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;
@@ -217,6 +257,26 @@ class AKSRT_Content_Visibility {
]);
}
// 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);
@@ -225,7 +285,12 @@ class AKSRT_Content_Visibility {
}
}
if (!empty($paid_settings['download_credit']) && $cost > 0) {
$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'), [