Allow pay_no_logged_in cash content orders (user_id=0), set aksrt_pay_* cookies on paid, accept cookie in access state, and expose guest CTA on paywall/box.
1255 lines
52 KiB
PHP
1255 lines
52 KiB
PHP
<?php
|
||
/**
|
||
* AKSRT Pay System — 统一付费内容系统(1:1 对齐 zibll)
|
||
*
|
||
* 核心机制:
|
||
* - 任何 post 通过 _aksrt_pay meta 成为付费文章
|
||
* - pay_type 驱动行为(no/reading/download/gallery/video/vip)
|
||
* - 统一的价格、VIP 价、下载源、属性、图库、视频管理
|
||
*
|
||
* @package AKSRT
|
||
*/
|
||
|
||
defined('ABSPATH') || exit;
|
||
|
||
// ============================================================
|
||
// 付费类型常量
|
||
// ============================================================
|
||
|
||
if (!defined('AKSRT_PAY_TYPE_NO')) {
|
||
define('AKSRT_PAY_TYPE_NO', AKSRT_Domain_Model::PAY_TYPE_NO);
|
||
}
|
||
if (!defined('AKSRT_PAY_TYPE_READING')) {
|
||
define('AKSRT_PAY_TYPE_READING', AKSRT_Domain_Model::PAY_TYPE_READING);
|
||
}
|
||
if (!defined('AKSRT_PAY_TYPE_DOWNLOAD')) {
|
||
define('AKSRT_PAY_TYPE_DOWNLOAD', AKSRT_Domain_Model::PAY_TYPE_DOWNLOAD);
|
||
}
|
||
if (!defined('AKSRT_PAY_TYPE_GALLERY')) {
|
||
define('AKSRT_PAY_TYPE_GALLERY', AKSRT_Domain_Model::PAY_TYPE_GALLERY);
|
||
}
|
||
if (!defined('AKSRT_PAY_TYPE_VIDEO')) {
|
||
define('AKSRT_PAY_TYPE_VIDEO', AKSRT_Domain_Model::PAY_TYPE_VIDEO);
|
||
}
|
||
if (!defined('AKSRT_PAY_TYPE_VIP')) {
|
||
define('AKSRT_PAY_TYPE_VIP', AKSRT_Domain_Model::PAY_TYPE_VIP);
|
||
}
|
||
|
||
/**
|
||
* 检查用户是否为 VIP 豁免用户(免扣积分下载)。
|
||
*
|
||
* @param int $user_id
|
||
* @return bool
|
||
*/
|
||
if (!function_exists('aksrt_is_vip_exempt')) {
|
||
function aksrt_is_vip_exempt(int $user_id = 0): bool {
|
||
if ($user_id <= 0) {
|
||
$user_id = get_current_user_id();
|
||
}
|
||
if ($user_id <= 0) {
|
||
return false;
|
||
}
|
||
// 优先使用 aksrt_is_download_credit_exempt_user(如果已定义)
|
||
if (function_exists('aksrt_is_download_credit_exempt_user')) {
|
||
return aksrt_is_download_credit_exempt_user($user_id);
|
||
}
|
||
// 回退:直接检查 VIP 角色
|
||
if (function_exists('aksrt_is_vip') && aksrt_is_vip($user_id)) {
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
}
|
||
|
||
function aksrt_get_pay_types(): array {
|
||
return AKSRT_Domain_Model::pay_type_labels();
|
||
|
||
return [
|
||
AKSRT_PAY_TYPE_NO => __('不付费', 'aksrt'),
|
||
AKSRT_PAY_TYPE_READING => __('付费阅读', 'aksrt'),
|
||
AKSRT_PAY_TYPE_DOWNLOAD => __('付费下载', 'aksrt'),
|
||
AKSRT_PAY_TYPE_GALLERY => __('付费图库', 'aksrt'),
|
||
AKSRT_PAY_TYPE_VIDEO => __('付费视频', 'aksrt'),
|
||
AKSRT_PAY_TYPE_VIP => __('VIP 专属', 'aksrt'),
|
||
];
|
||
}
|
||
|
||
// ============================================================
|
||
// 元数据读取
|
||
// ============================================================
|
||
|
||
if (!function_exists('aksrt_get_pay_meta')) {
|
||
function aksrt_get_pay_meta($post = null): array {
|
||
$p = get_post($post);
|
||
if (!($p instanceof WP_Post)) return aksrt_default_pay_meta();
|
||
|
||
$meta = get_post_meta($p->ID, AKSRT_Domain_Model::PAY_META_KEY, true);
|
||
if (is_array($meta) && isset($meta['pay_type'])) {
|
||
return wp_parse_args(AKSRT_Domain_Model::sanitize_pay_meta($meta), aksrt_default_pay_meta());
|
||
}
|
||
|
||
return aksrt_default_pay_meta();
|
||
}
|
||
}
|
||
|
||
if (!function_exists('aksrt_default_pay_meta')) {
|
||
function aksrt_default_pay_meta(): array {
|
||
return AKSRT_Domain_Model::default_pay_meta();
|
||
|
||
return [
|
||
'pay_type' => AKSRT_PAY_TYPE_NO,
|
||
'price' => 0.0,
|
||
'original_price' => 0.0,
|
||
'vip1_price' => 0.0,
|
||
'vip2_price' => 0.0,
|
||
'version' => '',
|
||
'size' => '',
|
||
'license' => '',
|
||
'compat' => '',
|
||
'demo_url' => '',
|
||
'downloads' => [],
|
||
'require_login' => false,
|
||
'require_captcha' => false,
|
||
'attributes' => [],
|
||
'gallery' => [],
|
||
'video_url' => '',
|
||
];
|
||
}
|
||
}
|
||
|
||
if (!function_exists('aksrt_is_downloadable_content')) {
|
||
function aksrt_is_downloadable_content($post = null): bool {
|
||
$p = get_post($post);
|
||
if (!($p instanceof WP_Post)) {
|
||
return false;
|
||
}
|
||
|
||
$meta = aksrt_get_pay_meta($p);
|
||
if (($meta['pay_type'] ?? AKSRT_PAY_TYPE_NO) !== AKSRT_PAY_TYPE_DOWNLOAD) {
|
||
return false;
|
||
}
|
||
|
||
return !empty($meta['downloads']) && is_array($meta['downloads']);
|
||
}
|
||
}
|
||
|
||
if (!function_exists('aksrt_get_download_box_meta')) {
|
||
function aksrt_get_download_box_meta($post = null): array {
|
||
$p = get_post($post);
|
||
if (!($p instanceof WP_Post)) {
|
||
return [];
|
||
}
|
||
|
||
if (function_exists('aksrt_get_resource_meta') && function_exists('aksrt_is_resource_post') && aksrt_is_resource_post($p)) {
|
||
$meta = aksrt_get_resource_meta($p);
|
||
$meta['subject_label'] = __('资源', 'aksrt');
|
||
return $meta;
|
||
}
|
||
|
||
$pay_meta = aksrt_get_pay_meta($p);
|
||
$download_count = (int) get_post_meta($p->ID, AKSRT_Domain_Model::RESOURCE_DOWNLOAD_COUNT_KEY, true);
|
||
$view_count = (int) get_post_meta($p->ID, AKSRT_Domain_Model::POST_VIEW_COUNT_KEY, true);
|
||
$virtual_sales = max(0, (int) ($pay_meta['virtual_sales'] ?? 0));
|
||
$sales_volume = (int) get_post_meta($p->ID, AKSRT_Domain_Model::RESOURCE_SALES_VOLUME_KEY, true);
|
||
$show_sales = !isset($pay_meta['show_sales']) || !empty($pay_meta['show_sales']);
|
||
$pay_modo = (($pay_meta['pay_modo'] ?? '0') === 'points') ? 'points' : '0';
|
||
$pay_limit = (int) ($pay_meta['pay_limit'] ?? 0);
|
||
if (!in_array($pay_limit, [0, 1, 2], true)) {
|
||
$pay_limit = 0;
|
||
}
|
||
$over_price = max(0.0, (float) ($pay_meta['download_limit_over_price'] ?? 0));
|
||
|
||
return [
|
||
'version' => (string) ($pay_meta['version'] ?? ''),
|
||
'size' => (string) ($pay_meta['size'] ?? ''),
|
||
'price' => (float) ($pay_meta['price'] ?? 0),
|
||
'points_price' => (int) ($pay_meta['points_price'] ?? 0),
|
||
'pay_modo' => $pay_modo,
|
||
'pay_limit' => $pay_limit,
|
||
'original_price' => (float) ($pay_meta['original_price'] ?? 0),
|
||
'vip1_price' => (float) ($pay_meta['vip1_price'] ?? 0),
|
||
'vip2_price' => (float) ($pay_meta['vip2_price'] ?? 0),
|
||
'vip1_points' => (int) ($pay_meta['vip1_points'] ?? 0),
|
||
'vip2_points' => (int) ($pay_meta['vip2_points'] ?? 0),
|
||
'pay_type' => (string) ($pay_meta['pay_type'] ?? AKSRT_PAY_TYPE_NO),
|
||
'license' => (string) ($pay_meta['license'] ?? ''),
|
||
'compat' => (string) ($pay_meta['compat'] ?? ''),
|
||
'demo_url' => (string) ($pay_meta['demo_url'] ?? ''),
|
||
'downloads' => array_values(is_array($pay_meta['downloads'] ?? null) ? $pay_meta['downloads'] : []),
|
||
'download_count' => max(0, $download_count),
|
||
'view_count' => max(0, $view_count),
|
||
'virtual_sales' => $virtual_sales,
|
||
'sales_volume' => max(0, $sales_volume),
|
||
// Zibll: virtual (pay_cuont) + real sales_volume; fall back to downloads when no sales yet.
|
||
'sales_count' => $show_sales ? ($virtual_sales + max(0, $sales_volume, $download_count)) : 0,
|
||
'show_sales' => $show_sales,
|
||
'download_limit_over_price' => $over_price,
|
||
'require_login' => !empty($pay_meta['require_login']),
|
||
'require_captcha' => !empty($pay_meta['require_captcha']),
|
||
'subject_label' => __('内容', 'aksrt'),
|
||
];
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Latest paid content order for a user/post (resource product_type).
|
||
*
|
||
* @return array<string,mixed>|null
|
||
*/
|
||
if (!function_exists('aksrt_get_user_content_paid_order')) {
|
||
function aksrt_get_user_content_paid_order(int $post_id, int $user_id = 0): ?array {
|
||
$uid = $user_id > 0 ? $user_id : get_current_user_id();
|
||
if ($uid <= 0 || $post_id <= 0 || !class_exists('AKSRT_Order_Repository')) {
|
||
return null;
|
||
}
|
||
$orders = AKSRT_Order_Repository::get_instance()->get_user_orders($uid, [
|
||
'product_type' => AKSRT_Domain_Model::ORDER_PRODUCT_RESOURCE,
|
||
'status' => '',
|
||
'limit' => 20,
|
||
'offset' => 0,
|
||
]);
|
||
foreach ($orders as $order) {
|
||
if ((int) ($order['product_id'] ?? 0) !== $post_id) {
|
||
continue;
|
||
}
|
||
$status = (string) ($order['status'] ?? '');
|
||
if (AKSRT_Domain_Model::is_paid_order_status($status)) {
|
||
return $order;
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Increment real sales volume for a paid content post (idempotent per order via order meta note flag optional).
|
||
*/
|
||
if (!function_exists('aksrt_bump_content_sales_volume')) {
|
||
function aksrt_bump_content_sales_volume(int $post_id, int $delta = 1): int {
|
||
if ($post_id <= 0 || $delta === 0) {
|
||
return 0;
|
||
}
|
||
$key = AKSRT_Domain_Model::RESOURCE_SALES_VOLUME_KEY;
|
||
$cur = (int) get_post_meta($post_id, $key, true);
|
||
$next = max(0, $cur + $delta);
|
||
update_post_meta($post_id, $key, $next);
|
||
return $next;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Whether guest cash purchase is allowed (Zibll pay_no_logged_in + pay_limit=0).
|
||
*/
|
||
if (!function_exists('aksrt_allow_guest_content_pay')) {
|
||
function aksrt_allow_guest_content_pay($post = null): bool {
|
||
$p = get_post($post);
|
||
if (!($p instanceof WP_Post)) {
|
||
return false;
|
||
}
|
||
$meta = function_exists('aksrt_get_pay_meta') ? aksrt_get_pay_meta($p) : [];
|
||
$pay_limit = (int) ($meta['pay_limit'] ?? 0);
|
||
if ($pay_limit > 0) {
|
||
return false;
|
||
}
|
||
$pay_modo = (($meta['pay_modo'] ?? '0') === 'points') ? 'points' : '0';
|
||
if ($pay_modo === 'points') {
|
||
return false; // points require login
|
||
}
|
||
$order = class_exists('AKSRT_Settings') ? AKSRT_Settings::get_order_settings() : [];
|
||
$paid = class_exists('AKSRT_Settings') ? AKSRT_Settings::get_paid_content_settings() : [];
|
||
$allow = !empty($order['no_logged_in']) || !empty($paid['no_logged_in']);
|
||
/**
|
||
* Filter guest content pay allow.
|
||
*
|
||
* @param bool $allow
|
||
* @param WP_Post $p
|
||
*/
|
||
return (bool) apply_filters('aksrt_allow_guest_content_pay', $allow, $p);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Cookie lifetime days for guest paid content (Zibll pay_cookie_day).
|
||
*/
|
||
if (!function_exists('aksrt_guest_pay_cookie_days')) {
|
||
function aksrt_guest_pay_cookie_days(int $post_id = 0): int {
|
||
$days = 15;
|
||
if (class_exists('AKSRT_Settings')) {
|
||
$order = AKSRT_Settings::get_order_settings();
|
||
$days = max(1, min(31, (int) ($order['cookie_day'] ?? 15)));
|
||
}
|
||
/**
|
||
* @param int $days
|
||
* @param int $post_id
|
||
*/
|
||
return max(1, (int) apply_filters('aksrt_guest_pay_cookie_days', $days, $post_id));
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Client IP helper for cookie token binding.
|
||
*/
|
||
if (!function_exists('aksrt_pay_client_ip')) {
|
||
function aksrt_pay_client_ip(): string {
|
||
if (function_exists('aksrt_get_client_ip')) {
|
||
return (string) aksrt_get_client_ip();
|
||
}
|
||
return (string) ($_SERVER['REMOTE_ADDR'] ?? '');
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Build token material for guest order cookie (zibll-style).
|
||
*
|
||
* @param array<string,mixed> $order
|
||
*/
|
||
if (!function_exists('aksrt_content_order_cookie_token_key')) {
|
||
function aksrt_content_order_cookie_token_key(array $order): string {
|
||
$meta = is_array($order['meta_payload'] ?? null) ? $order['meta_payload'] : [];
|
||
$ip = (string) ($meta['ip_address'] ?? $order['ip_address'] ?? '');
|
||
$id = (string) ($order['id'] ?? $order['order_number'] ?? '');
|
||
$post_id = (int) ($order['product_id'] ?? $meta['post_id'] ?? 0);
|
||
$num = (string) ($order['order_number'] ?? '');
|
||
$amount = function_exists('aksrt_format_price')
|
||
? aksrt_format_price((float) ($order['amount'] ?? 0))
|
||
: (string) round((float) ($order['amount'] ?? 0), 2);
|
||
return $id . '_' . $post_id . '_' . trim($num) . '_' . $amount . '_' . trim($ip);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Set guest paid cookies after content order paid (Zibll zibpay_posts_order_created_set_cookie).
|
||
*
|
||
* @param array<string,mixed> $order
|
||
*/
|
||
if (!function_exists('aksrt_set_guest_content_pay_cookies')) {
|
||
function aksrt_set_guest_content_pay_cookies(array $order): void {
|
||
$post_id = (int) ($order['product_id'] ?? 0);
|
||
$order_number = (string) ($order['order_number'] ?? '');
|
||
if ($post_id <= 0 || $order_number === '') {
|
||
return;
|
||
}
|
||
$days = aksrt_guest_pay_cookie_days($post_id);
|
||
$expire = time() + (DAY_IN_SECONDS * $days);
|
||
$path = defined('COOKIEPATH') && COOKIEPATH ? COOKIEPATH : '/';
|
||
$domain = defined('COOKIE_DOMAIN') ? COOKIE_DOMAIN : '';
|
||
$secure = is_ssl();
|
||
$httponly = true;
|
||
|
||
setcookie('aksrt_pay_' . $post_id, $order_number, $expire, $path, $domain, $secure, $httponly);
|
||
$_COOKIE['aksrt_pay_' . $post_id] = $order_number;
|
||
|
||
$token = md5(aksrt_content_order_cookie_token_key($order));
|
||
$token_name = 'aksrt_pay_token_' . md5($order_number);
|
||
setcookie($token_name, $token, $expire, $path, $domain, $secure, $httponly);
|
||
$_COOKIE[$token_name] = $token;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Resolve guest paid order from browser cookie for a post.
|
||
*
|
||
* @return array<string,mixed>|null
|
||
*/
|
||
if (!function_exists('aksrt_get_guest_content_paid_order')) {
|
||
function aksrt_get_guest_content_paid_order(int $post_id): ?array {
|
||
if ($post_id <= 0 || empty($_COOKIE['aksrt_pay_' . $post_id])) {
|
||
return null;
|
||
}
|
||
if (!class_exists('AKSRT_Order_Repository')) {
|
||
return null;
|
||
}
|
||
$order_number = sanitize_text_field((string) $_COOKIE['aksrt_pay_' . $post_id]);
|
||
if ($order_number === '') {
|
||
return null;
|
||
}
|
||
$order = AKSRT_Order_Repository::get_instance()->get($order_number);
|
||
if (!$order) {
|
||
return null;
|
||
}
|
||
if ((string) ($order['product_type'] ?? '') !== AKSRT_Domain_Model::ORDER_PRODUCT_RESOURCE) {
|
||
return null;
|
||
}
|
||
if ((int) ($order['product_id'] ?? 0) !== $post_id) {
|
||
return null;
|
||
}
|
||
$status = (string) ($order['status'] ?? '');
|
||
if (!AKSRT_Domain_Model::is_paid_order_status($status)) {
|
||
return null;
|
||
}
|
||
|
||
// Cookie day window from paid_at.
|
||
$paid_at = (string) ($order['paid_at'] ?? $order['created_at'] ?? '');
|
||
if ($paid_at !== '') {
|
||
$ts = strtotime($paid_at);
|
||
$days = aksrt_guest_pay_cookie_days($post_id);
|
||
if ($ts !== false && ($ts + DAY_IN_SECONDS * $days) < time()) {
|
||
return null;
|
||
}
|
||
}
|
||
|
||
$token_name = 'aksrt_pay_token_' . md5($order_number);
|
||
$cookie_token = isset($_COOKIE[$token_name]) ? (string) $_COOKIE[$token_name] : '';
|
||
$expect = md5(aksrt_content_order_cookie_token_key($order));
|
||
if ($cookie_token === '' || !hash_equals($expect, $cookie_token)) {
|
||
return null;
|
||
}
|
||
|
||
return $order;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Whether current request has paid access via guest cookie.
|
||
*/
|
||
if (!function_exists('aksrt_guest_has_paid_access')) {
|
||
function aksrt_guest_has_paid_access(int $post_id): bool {
|
||
return null !== aksrt_get_guest_content_paid_order($post_id);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Resolve the amount a given user should pay for a post (Zibll VIP tier min).
|
||
*
|
||
* VIP tier prices only apply when configured as a positive discount (< base).
|
||
* Full free for VIP uses vip_free_download / exempt roles (handled separately).
|
||
*
|
||
* @param int|WP_Post|null $post
|
||
* @param int $user_id
|
||
* @return array{amount:float,base:float,vip_amount:float,modo:string,currency:string,is_free:bool,pay_limit:int,vip_level:int,original:float,vip1:float,vip2:float}
|
||
*/
|
||
if (!function_exists('aksrt_get_user_pay_price')) {
|
||
function aksrt_get_user_pay_price($post = null, int $user_id = 0): array {
|
||
$meta = function_exists('aksrt_get_pay_meta') ? aksrt_get_pay_meta($post) : AKSRT_Domain_Model::default_pay_meta();
|
||
$pay_modo = (($meta['pay_modo'] ?? '0') === 'points') ? 'points' : '0';
|
||
$pay_limit = (int) ($meta['pay_limit'] ?? 0);
|
||
if (!in_array($pay_limit, [0, 1, 2], true)) {
|
||
$pay_limit = 0;
|
||
}
|
||
|
||
$uid = $user_id > 0 ? $user_id : (function_exists('get_current_user_id') ? (int) get_current_user_id() : 0);
|
||
$vip_level = 0;
|
||
if ($uid > 0 && function_exists('aksrt_get_user_vip_level')) {
|
||
$vip_level = (int) aksrt_get_user_vip_level($uid);
|
||
}
|
||
if ($uid > 0 && function_exists('aksrt_is_vip_exempt') && aksrt_is_vip_exempt($uid)) {
|
||
$vip_level = max($vip_level, 2);
|
||
}
|
||
|
||
$currency = class_exists('AKSRT_Settings') ? AKSRT_Settings::get_currency_name() : __('积分', 'aksrt');
|
||
$cash_symbol = '¥';
|
||
if (class_exists('AKSRT_Settings')) {
|
||
$paid = AKSRT_Settings::get_paid_content_settings();
|
||
$cash_symbol = (string) ($paid['display']['currency_symbol'] ?? '¥');
|
||
}
|
||
|
||
if ($pay_modo === 'points') {
|
||
$base = (float) ($meta['points_price'] ?? 0);
|
||
$vip1 = (float) ($meta['vip1_points'] ?? 0);
|
||
$vip2 = (float) ($meta['vip2_points'] ?? 0);
|
||
$currency_label = $currency;
|
||
} else {
|
||
$base = (float) ($meta['price'] ?? 0);
|
||
$vip1 = (float) ($meta['vip1_price'] ?? 0);
|
||
$vip2 = (float) ($meta['vip2_price'] ?? 0);
|
||
$currency_label = $cash_symbol;
|
||
}
|
||
|
||
// Positive VIP tier prices only (0 defaults mean "not configured").
|
||
$vip_amount = -1.0;
|
||
if ($vip_level >= 2 && $vip2 > 0) {
|
||
$vip_amount = $vip2;
|
||
} elseif ($vip_level >= 1 && $vip1 > 0) {
|
||
$vip_amount = $vip1;
|
||
}
|
||
|
||
$amount = $base;
|
||
if ($vip_amount >= 0 && ($base <= 0 || $vip_amount < $base)) {
|
||
$amount = $vip_amount;
|
||
}
|
||
|
||
// Role/VIP free-download exemption (global setting).
|
||
if ($amount > 0 && $uid > 0 && function_exists('aksrt_is_download_credit_exempt_user') && aksrt_is_download_credit_exempt_user($uid)) {
|
||
$amount = 0.0;
|
||
}
|
||
|
||
$original = (float) ($meta['original_price'] ?? 0);
|
||
|
||
return [
|
||
'amount' => max(0.0, (float) $amount),
|
||
'base' => max(0.0, (float) $base),
|
||
'vip_amount' => $vip_amount >= 0 ? max(0.0, (float) $vip_amount) : -1.0,
|
||
'modo' => $pay_modo,
|
||
'currency' => $currency_label,
|
||
'is_free' => $amount <= 0,
|
||
'pay_limit' => $pay_limit,
|
||
'vip_level' => $vip_level,
|
||
'original' => $original,
|
||
'vip1' => max(0.0, (float) $vip1),
|
||
'vip2' => max(0.0, (float) $vip2),
|
||
];
|
||
}
|
||
}
|
||
|
||
// ============================================================
|
||
// 状态判断
|
||
// ============================================================
|
||
|
||
if (!function_exists('aksrt_is_paid_post')) {
|
||
function aksrt_is_paid_post($post = null): bool {
|
||
return aksrt_get_pay_meta($post)['pay_type'] !== AKSRT_PAY_TYPE_NO;
|
||
}
|
||
}
|
||
|
||
if (!function_exists('aksrt_is_paid_reading')) {
|
||
function aksrt_is_paid_reading($post = null): bool {
|
||
return aksrt_get_pay_meta($post)['pay_type'] === AKSRT_PAY_TYPE_READING;
|
||
}
|
||
}
|
||
|
||
if (!function_exists('aksrt_is_paid_download')) {
|
||
function aksrt_is_paid_download($post = null): bool {
|
||
return aksrt_get_pay_meta($post)['pay_type'] === AKSRT_PAY_TYPE_DOWNLOAD;
|
||
}
|
||
}
|
||
|
||
if (!function_exists('aksrt_is_paid_gallery')) {
|
||
function aksrt_is_paid_gallery($post = null): bool {
|
||
return aksrt_get_pay_meta($post)['pay_type'] === AKSRT_PAY_TYPE_GALLERY;
|
||
}
|
||
}
|
||
|
||
if (!function_exists('aksrt_is_paid_video')) {
|
||
function aksrt_is_paid_video($post = null): bool {
|
||
return aksrt_get_pay_meta($post)['pay_type'] === AKSRT_PAY_TYPE_VIDEO;
|
||
}
|
||
}
|
||
|
||
if (!function_exists('aksrt_is_vip_only')) {
|
||
function aksrt_is_vip_only($post = null): bool {
|
||
return aksrt_get_pay_meta($post)['pay_type'] === AKSRT_PAY_TYPE_VIP;
|
||
}
|
||
}
|
||
|
||
// ============================================================
|
||
// 付费状态检查
|
||
// ============================================================
|
||
|
||
if (!function_exists('aksrt_has_paid_access')) {
|
||
function aksrt_has_paid_access($post = null, int $user_id = 0): bool {
|
||
$p = get_post($post);
|
||
if (!($p instanceof WP_Post)) return true;
|
||
|
||
// 单一真相:付费访问由 AKSRT_Content_Visibility(基于订单 + VIP)裁决。
|
||
if (class_exists('AKSRT_Content_Visibility')) {
|
||
$state = AKSRT_Content_Visibility::get_post_access_state($p, $user_id);
|
||
return !empty($state['has_paid_access']);
|
||
}
|
||
|
||
// 兜底:仅在 service 不可用时使用,避免离线环境直接拒绝。
|
||
$meta = aksrt_get_pay_meta($p);
|
||
if ($meta['pay_type'] === AKSRT_PAY_TYPE_NO) return true;
|
||
if ((float) $meta['price'] <= 0 && $meta['pay_type'] !== AKSRT_PAY_TYPE_VIP) return true;
|
||
|
||
$uid = $user_id > 0 ? $user_id : get_current_user_id();
|
||
|
||
if ($uid > 0 && function_exists('aksrt_is_vip_exempt') && aksrt_is_vip_exempt($uid)) return true;
|
||
if ($uid > 0 && get_user_meta($uid, '_aksrt_paid_' . $p->ID, true)) return true;
|
||
|
||
return false;
|
||
}
|
||
}
|
||
|
||
if (!function_exists('aksrt_can_access_content')) {
|
||
function aksrt_can_access_content($post = null): bool {
|
||
$p = get_post($post);
|
||
if (!($p instanceof WP_Post)) return true;
|
||
|
||
if (class_exists('AKSRT_Content_Visibility')) {
|
||
return AKSRT_Content_Visibility::can_view_paid_content($p);
|
||
}
|
||
|
||
$pay_type = aksrt_get_pay_meta($p)['pay_type'];
|
||
if ($pay_type === AKSRT_PAY_TYPE_NO) return true;
|
||
|
||
// 只有 reading/gallery/video 需要隐藏内容
|
||
if (!in_array($pay_type, [AKSRT_PAY_TYPE_READING, AKSRT_PAY_TYPE_GALLERY, AKSRT_PAY_TYPE_VIDEO], true)) {
|
||
return true;
|
||
}
|
||
|
||
return aksrt_has_paid_access($p);
|
||
}
|
||
}
|
||
|
||
if (!function_exists('aksrt_can_download')) {
|
||
/**
|
||
* 下载授权门禁 —— 单一合同。
|
||
*
|
||
* 实际判定全部交给 AKSRT_Content_Visibility::can_download_resource()
|
||
* (由 inc/helpers/aksrt-resource.php 的同名 helper 在更早加载时定义)。
|
||
* 此处仅作为该 helper 不可用时的兜底,避免出现第二套下载真相。
|
||
*
|
||
* @param int|WP_Post|null $post
|
||
* @param int $user_id 0 表示当前用户
|
||
* @return true|WP_Error
|
||
*/
|
||
function aksrt_can_download($post = null, int $user_id = 0) {
|
||
if (class_exists('AKSRT_Content_Visibility')) {
|
||
return AKSRT_Content_Visibility::can_download_resource($post, $user_id);
|
||
}
|
||
|
||
$p = get_post($post);
|
||
if (!($p instanceof WP_Post)) {
|
||
return new WP_Error('aksrt_invalid', __('无效内容。', 'aksrt'));
|
||
}
|
||
|
||
$meta = aksrt_get_pay_meta($p);
|
||
if ($meta['pay_type'] !== AKSRT_PAY_TYPE_DOWNLOAD) {
|
||
return new WP_Error('aksrt_not_downloadable', __('该内容不是可下载资源。', 'aksrt'));
|
||
}
|
||
|
||
$paid_settings = class_exists('AKSRT_Settings')
|
||
? AKSRT_Settings::get_paid_content_settings()
|
||
: ['download_login' => true, 'download_credit' => true];
|
||
|
||
$uid = $user_id > 0 ? $user_id : get_current_user_id();
|
||
|
||
if (!empty($paid_settings['download_login']) && $uid <= 0) {
|
||
$login_url = wp_login_url(get_permalink($p));
|
||
return new WP_Error('aksrt_need_login', __('请先登录。', 'aksrt'), ['login_url' => $login_url]);
|
||
}
|
||
|
||
if ($uid > 0 && aksrt_has_paid_access($p, $uid)) return true;
|
||
if ($uid > 0 && function_exists('aksrt_is_vip_exempt') && aksrt_is_vip_exempt($uid)) return true;
|
||
|
||
$cost = aksrt_get_download_cost($p);
|
||
if ($cost > 0 && !empty($paid_settings['download_credit'])) {
|
||
$balance = function_exists('aksrt_user_credits') ? aksrt_user_credits($uid) : 0;
|
||
if ($balance < $cost) {
|
||
return new WP_Error('aksrt_credits_insufficient', __('积分不足。', 'aksrt'), [
|
||
'balance' => $balance,
|
||
'cost' => $cost,
|
||
]);
|
||
}
|
||
}
|
||
|
||
return true;
|
||
}
|
||
}
|
||
|
||
if (!function_exists('aksrt_get_download_cost')) {
|
||
function aksrt_get_download_cost($post = null): int {
|
||
if (function_exists('aksrt_resource_download_cost')) {
|
||
return (int) aksrt_resource_download_cost($post);
|
||
}
|
||
|
||
$meta = aksrt_get_pay_meta($post);
|
||
return (int) ceil((float) $meta['price']);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* URL of the Zibll-style single-post download page (page template + ?post=ID).
|
||
*/
|
||
if (!function_exists('aksrt_get_single_download_page_url')) {
|
||
function aksrt_get_single_download_page_url($post = null): string {
|
||
$p = get_post($post);
|
||
if (!($p instanceof WP_Post)) {
|
||
return '';
|
||
}
|
||
|
||
$page_url = '';
|
||
if (function_exists('aksrt_find_template_page')) {
|
||
$page = aksrt_find_template_page('templates/pages/page-download-file.php');
|
||
if ($page instanceof WP_Post) {
|
||
$page_url = get_permalink($page);
|
||
}
|
||
}
|
||
if ($page_url === '') {
|
||
// Fallback: look for any page using the template via slug convention.
|
||
$page_url = home_url('/download-file/');
|
||
}
|
||
|
||
return add_query_arg('post', (int) $p->ID, $page_url);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Whether independent download page mode is enabled (Zibll down_alone_page).
|
||
*/
|
||
if (!function_exists('aksrt_is_download_alone_page_enabled')) {
|
||
function aksrt_is_download_alone_page_enabled(): bool {
|
||
if (!class_exists('AKSRT_Settings')) {
|
||
return false;
|
||
}
|
||
$paid = AKSRT_Settings::get_paid_content_settings();
|
||
return !empty($paid['down_alone_page']) || !empty($paid['download']['alone_page']);
|
||
}
|
||
}
|
||
|
||
// ============================================================
|
||
// 价格显示
|
||
// ============================================================
|
||
|
||
if (!function_exists('aksrt_get_pay_price_label')) {
|
||
function aksrt_get_pay_price_label($post = null): array {
|
||
$meta = aksrt_get_pay_meta($post);
|
||
$priced = function_exists('aksrt_get_user_pay_price')
|
||
? aksrt_get_user_pay_price($post)
|
||
: null;
|
||
$pay_modo = is_array($priced)
|
||
? ((($priced['modo'] ?? '0') === 'points') ? 'points' : '0')
|
||
: ((($meta['pay_modo'] ?? '0') === 'points') ? 'points' : '0');
|
||
$pay_limit = is_array($priced)
|
||
? (int) ($priced['pay_limit'] ?? 0)
|
||
: (int) ($meta['pay_limit'] ?? 0);
|
||
$vip_level = is_array($priced) ? (int) ($priced['vip_level'] ?? 0) : 0;
|
||
$currency = class_exists('AKSRT_Settings') ? AKSRT_Settings::get_currency_name() : __('积分', 'aksrt');
|
||
$cash_symbol = '¥';
|
||
if (class_exists('AKSRT_Settings')) {
|
||
$paid = AKSRT_Settings::get_paid_content_settings();
|
||
$cash_symbol = (string) ($paid['display']['currency_symbol'] ?? '¥');
|
||
}
|
||
|
||
// pay_type VIP-only or pay_limit not met → member-exclusive badge.
|
||
$member_blocked = ($meta['pay_type'] === AKSRT_PAY_TYPE_VIP)
|
||
|| ($pay_limit > 0 && $vip_level < $pay_limit);
|
||
if ($member_blocked) {
|
||
return [
|
||
'text' => __('会员专属', 'aksrt'),
|
||
'free' => false,
|
||
'amount' => 0,
|
||
'base' => is_array($priced) ? (float) ($priced['base'] ?? 0) : 0,
|
||
'modo' => $pay_modo,
|
||
'pay_limit' => $pay_limit,
|
||
];
|
||
}
|
||
|
||
$price = is_array($priced)
|
||
? (float) ($priced['amount'] ?? 0)
|
||
: ($pay_modo === 'points' ? (float) ($meta['points_price'] ?? 0) : (float) ($meta['price'] ?? 0));
|
||
$base = is_array($priced) ? (float) ($priced['base'] ?? $price) : $price;
|
||
|
||
if ($price <= 0) {
|
||
return [
|
||
'text' => __('免费', 'aksrt'),
|
||
'free' => true,
|
||
'amount' => 0,
|
||
'base' => $base,
|
||
'modo' => $pay_modo,
|
||
'pay_limit' => $pay_limit,
|
||
];
|
||
}
|
||
|
||
if ($pay_modo === 'points') {
|
||
return [
|
||
'text' => sprintf(__('%s %s', 'aksrt'), aksrt_format_price($price), $currency),
|
||
'free' => false,
|
||
'amount' => $price,
|
||
'base' => $base,
|
||
'modo' => 'points',
|
||
'pay_limit' => $pay_limit,
|
||
];
|
||
}
|
||
|
||
return [
|
||
'text' => $cash_symbol . aksrt_format_price($price),
|
||
'free' => false,
|
||
'amount' => $price,
|
||
'base' => $base,
|
||
'modo' => '0',
|
||
'pay_limit' => $pay_limit,
|
||
];
|
||
}
|
||
}
|
||
|
||
if (!function_exists('aksrt_format_price')) {
|
||
function aksrt_format_price(float $price): string {
|
||
if (fmod($price, 1.0) === 0.0) return (string) (int) $price;
|
||
return rtrim(rtrim(number_format($price, 2, '.', ''), '0'), '.');
|
||
}
|
||
}
|
||
|
||
// ============================================================
|
||
// 付费类型徽章
|
||
// ============================================================
|
||
|
||
if (!function_exists('aksrt_get_pay_type_badge')) {
|
||
function aksrt_get_pay_type_badge(string $pay_type): string {
|
||
$badges = [
|
||
AKSRT_PAY_TYPE_READING => ['label' => __('付费阅读', 'aksrt'), 'color' => '#EF4444', 'icon' => 'fa-lock'],
|
||
AKSRT_PAY_TYPE_DOWNLOAD => ['label' => __('付费下载', 'aksrt'), 'color' => '#F97316', 'icon' => 'fa-download'],
|
||
AKSRT_PAY_TYPE_GALLERY => ['label' => __('付费图库', 'aksrt'), 'color' => '#10B981', 'icon' => 'fa-images'],
|
||
AKSRT_PAY_TYPE_VIDEO => ['label' => __('付费视频', 'aksrt'), 'color' => '#8B5CF6', 'icon' => 'fa-play-circle'],
|
||
AKSRT_PAY_TYPE_VIP => ['label' => __('VIP 专属', 'aksrt'), 'color' => '#3B82F6', 'icon' => 'fa-crown'],
|
||
];
|
||
|
||
if (!isset($badges[$pay_type])) return '';
|
||
$b = $badges[$pay_type];
|
||
return sprintf(
|
||
'<span class="aksrt-pay-badge" style="background:%s"><i class="fas %s"></i> %s</span>',
|
||
esc_attr($b['color']), esc_attr($b['icon']), esc_html($b['label'])
|
||
);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Zibll zibpay_get_service() — service bullets under pay/download box.
|
||
*
|
||
* @return string HTML list of service items.
|
||
*/
|
||
if (!function_exists('aksrt_get_pay_service_html')) {
|
||
function aksrt_get_pay_service_html(string $class = 'resource-download-box__guarantee'): string {
|
||
$service = [];
|
||
if (class_exists('AKSRT_Settings')) {
|
||
$order = AKSRT_Settings::get_order_settings();
|
||
$service = is_array($order['service'] ?? null) ? $order['service'] : [];
|
||
}
|
||
|
||
$items = [];
|
||
$text = trim((string) ($service['text'] ?? ''));
|
||
$value = trim((string) ($service['value'] ?? ''));
|
||
$icon = trim((string) ($service['icon'] ?? 'fas fa-check-circle'));
|
||
if ($icon === '') {
|
||
$icon = 'fas fa-check-circle';
|
||
}
|
||
|
||
// Support multi-line pay_service textarea (one bullet per line).
|
||
$lines = [];
|
||
if ($text !== '') {
|
||
$lines = preg_split('/\r\n|\r|\n/', $text) ?: [];
|
||
}
|
||
if ($value !== '') {
|
||
$lines[] = $value;
|
||
}
|
||
$lines = array_values(array_filter(array_map('trim', $lines)));
|
||
|
||
if ($lines === []) {
|
||
// Defaults aligned with previous hardcoded guarantees.
|
||
$lines = [
|
||
__('安全检测', 'aksrt'),
|
||
__('支持 VIP 权益', 'aksrt'),
|
||
__('支持积分结算', 'aksrt'),
|
||
];
|
||
}
|
||
|
||
$html = '';
|
||
foreach ($lines as $line) {
|
||
$html .= '<span class="' . esc_attr($class) . '">';
|
||
$html .= '<i class="' . esc_attr($icon) . '" aria-hidden="true"></i> ';
|
||
$html .= esc_html($line);
|
||
$html .= '</span>';
|
||
}
|
||
|
||
return $html;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Demo link button (Zibll zibpay_get_demo_link).
|
||
*/
|
||
if (!function_exists('aksrt_get_demo_link_html')) {
|
||
function aksrt_get_demo_link_html($post = null, string $class = 'resource-download-box__btn btn-outline resource-download-box__btn--demo'): string {
|
||
$p = get_post($post);
|
||
if (!($p instanceof WP_Post)) {
|
||
return '';
|
||
}
|
||
$meta = aksrt_get_pay_meta($p);
|
||
if (($meta['pay_type'] ?? '') !== AKSRT_PAY_TYPE_DOWNLOAD) {
|
||
return '';
|
||
}
|
||
$url = trim((string) ($meta['demo_url'] ?? ''));
|
||
if ($url === '') {
|
||
return '';
|
||
}
|
||
$text = __('查看演示', 'aksrt');
|
||
return sprintf(
|
||
'<a target="_blank" rel="nofollow noopener" href="%s" class="%s"><i class="fas fa-external-link-alt" aria-hidden="true"></i><span>%s</span></a>',
|
||
esc_url($url),
|
||
esc_attr($class),
|
||
esc_html($text)
|
||
);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Product attributes row (Zibll zibpay_get_product_attributes).
|
||
*/
|
||
if (!function_exists('aksrt_get_pay_attributes_html')) {
|
||
function aksrt_get_pay_attributes_html($post = null, string $class = 'resource-download-box__attrs'): string {
|
||
$p = get_post($post);
|
||
if (!($p instanceof WP_Post)) {
|
||
return '';
|
||
}
|
||
$meta = aksrt_get_pay_meta($p);
|
||
$attrs = is_array($meta['attributes'] ?? null) ? $meta['attributes'] : [];
|
||
// Built-in version/size/license/compat as attributes when set.
|
||
$builtin = [
|
||
'version' => __('版本', 'aksrt'),
|
||
'size' => __('大小', 'aksrt'),
|
||
'license' => __('授权', 'aksrt'),
|
||
'compat' => __('兼容', 'aksrt'),
|
||
];
|
||
$rows = [];
|
||
foreach ($builtin as $key => $label) {
|
||
$val = trim((string) ($meta[$key] ?? ''));
|
||
if ($val !== '') {
|
||
$rows[] = ['name' => $label, 'value' => $val];
|
||
}
|
||
}
|
||
foreach ($attrs as $row) {
|
||
if (!is_array($row)) {
|
||
continue;
|
||
}
|
||
$name = trim((string) ($row['name'] ?? $row['key'] ?? $row['label'] ?? ''));
|
||
$value = trim((string) ($row['value'] ?? $row['val'] ?? ''));
|
||
if ($name === '' || $value === '') {
|
||
continue;
|
||
}
|
||
$rows[] = ['name' => $name, 'value' => $value];
|
||
}
|
||
if ($rows === []) {
|
||
return '';
|
||
}
|
||
$html = '<ul class="' . esc_attr($class) . '">';
|
||
foreach ($rows as $row) {
|
||
$html .= '<li class="' . esc_attr($class) . '-item">';
|
||
$html .= '<span class="' . esc_attr($class) . '-name">' . esc_html($row['name']) . '</span>';
|
||
$html .= '<span class="' . esc_attr($class) . '-value">' . esc_html($row['value']) . '</span>';
|
||
$html .= '</li>';
|
||
}
|
||
$html .= '</ul>';
|
||
return $html;
|
||
}
|
||
}
|
||
|
||
// ============================================================
|
||
// 付费墙 HTML
|
||
// ============================================================
|
||
|
||
if (!function_exists('aksrt_get_paywall_html')) {
|
||
function aksrt_get_paywall_html(WP_Post $post): string {
|
||
$meta = aksrt_get_pay_meta($post);
|
||
$pay_type = $meta['pay_type'];
|
||
$priced = function_exists('aksrt_get_user_pay_price') ? aksrt_get_user_pay_price($post) : null;
|
||
$pay_modo = is_array($priced) ? ((($priced['modo'] ?? '0') === 'points') ? 'points' : '0') : ((($meta['pay_modo'] ?? '0') === 'points') ? 'points' : '0');
|
||
$price = is_array($priced) ? (float) ($priced['amount'] ?? 0) : (float) ($meta['price'] ?? 0);
|
||
$vip_level = is_array($priced) ? (int) ($priced['vip_level'] ?? 0) : 0;
|
||
$currency = class_exists('AKSRT_Settings') ? AKSRT_Settings::get_currency_name() : __('积分', 'aksrt');
|
||
$cash_symbol = '¥';
|
||
if (class_exists('AKSRT_Settings')) {
|
||
$paid = AKSRT_Settings::get_paid_content_settings();
|
||
$cash_symbol = (string) ($paid['display']['currency_symbol'] ?? '¥');
|
||
}
|
||
$price_text = $pay_modo === 'points'
|
||
? (aksrt_format_price($price) . ' ' . $currency)
|
||
: ($cash_symbol . aksrt_format_price($price));
|
||
|
||
$type_labels = [
|
||
AKSRT_PAY_TYPE_READING => ['icon' => 'fa-lock', 'label' => __('付费阅读', 'aksrt'), 'desc' => __('付费后即可查看完整内容', 'aksrt')],
|
||
AKSRT_PAY_TYPE_GALLERY => ['icon' => 'fa-images', 'label' => __('付费图库', 'aksrt'), 'desc' => __('付费后即可查看全部图片', 'aksrt')],
|
||
AKSRT_PAY_TYPE_VIDEO => ['icon' => 'fa-play-circle', 'label' => __('付费视频', 'aksrt'), 'desc' => __('付费后即可观看完整视频', 'aksrt')],
|
||
];
|
||
|
||
$type_info = $type_labels[$pay_type] ?? $type_labels[AKSRT_PAY_TYPE_READING];
|
||
$login_url = function_exists('aksrt_get_login_url') ? aksrt_get_login_url() : wp_login_url(get_permalink($post));
|
||
$is_logged_in = is_user_logged_in();
|
||
$original_price = (float) ($meta['original_price'] ?? 0);
|
||
$has_original = $original_price > 0 && $original_price > $price;
|
||
$pay_limit = (int) ($meta['pay_limit'] ?? 0);
|
||
$member_blocked = $pay_limit > 0 && $vip_level < $pay_limit;
|
||
$vip_url = function_exists('aksrt_get_user_center_url') ? aksrt_get_user_center_url() : home_url('/');
|
||
|
||
ob_start();
|
||
?>
|
||
<div class="aksrt-paywall" data-post-id="<?php echo esc_attr((string) $post->ID); ?>" data-pay-type="<?php echo esc_attr($pay_type); ?>" data-pay-modo="<?php echo esc_attr($pay_modo); ?>">
|
||
<div class="aksrt-paywall__overlay">
|
||
<div class="aksrt-paywall__blur"></div>
|
||
<div class="aksrt-paywall__card">
|
||
<div class="aksrt-paywall__icon">
|
||
<i class="fas <?php echo esc_attr($type_info['icon']); ?>" aria-hidden="true"></i>
|
||
</div>
|
||
<h3 class="aksrt-paywall__title"><?php echo esc_html($type_info['label']); ?></h3>
|
||
<p class="aksrt-paywall__desc"><?php echo esc_html($type_info['desc']); ?></p>
|
||
<div class="aksrt-paywall__price">
|
||
<?php if ($member_blocked) : ?>
|
||
<span class="aksrt-paywall__price-member"><?php esc_html_e('会员专属', 'aksrt'); ?></span>
|
||
<?php elseif ($price > 0) : ?>
|
||
<span class="aksrt-paywall__price-amount"><?php echo esc_html($price_text); ?></span>
|
||
<?php if ($has_original) : ?>
|
||
<span class="aksrt-paywall__price-original"><?php echo esc_html(($pay_modo === 'points' ? aksrt_format_price($original_price) . ' ' . $currency : $cash_symbol . aksrt_format_price($original_price))); ?></span>
|
||
<?php endif; ?>
|
||
<?php else : ?>
|
||
<span class="aksrt-paywall__price-free"><?php esc_html_e('免费', 'aksrt'); ?></span>
|
||
<?php endif; ?>
|
||
</div>
|
||
<?php if (!$is_logged_in) : ?>
|
||
<?php if ($pay_limit <= 0 && $price > 0 && $pay_modo !== 'points' && function_exists('aksrt_allow_guest_content_pay') && aksrt_allow_guest_content_pay($post)) : ?>
|
||
<button type="button" class="but <?php echo $pay_modo === 'points' ? 'jb-yellow' : 'jb-red'; ?> padding-lg cashier-link aksrt-paywall__btn" data-aksrt-pay-content="<?php echo esc_attr((string) $post->ID); ?>">
|
||
<i class="fa fa-shopping-cart" aria-hidden="true"></i> <?php printf(esc_html__('游客购买(%s)', 'aksrt'), $price_text); ?>
|
||
</button>
|
||
<p class="aksrt-paywall__hint" style="margin-top:10px;">
|
||
<a href="<?php echo esc_url($login_url); ?>"><?php esc_html_e('已有账号?登录后购买', 'aksrt'); ?></a>
|
||
</p>
|
||
<?php else : ?>
|
||
<a href="<?php echo esc_url($login_url); ?>" class="but jb-red padding-lg aksrt-paywall__btn">
|
||
<i class="fa fa-sign-in" aria-hidden="true"></i> <?php esc_html_e('登录后购买', 'aksrt'); ?>
|
||
</a>
|
||
<?php endif; ?>
|
||
<?php elseif ($member_blocked) : ?>
|
||
<a href="<?php echo esc_url($vip_url); ?>" class="but jb-vip padding-lg aksrt-paywall__btn">
|
||
<i class="fas fa-crown" aria-hidden="true"></i> <?php esc_html_e('开通会员后可查看', 'aksrt'); ?>
|
||
</a>
|
||
<?php elseif ($price > 0) : ?>
|
||
<button type="button" class="but <?php echo $pay_modo === 'points' ? 'jb-yellow' : 'jb-red'; ?> padding-lg cashier-link aksrt-paywall__btn" data-aksrt-pay-content="<?php echo esc_attr((string) $post->ID); ?>">
|
||
<i class="fa fa-shopping-cart" aria-hidden="true"></i> <?php printf(esc_html__('立即购买(%s)', 'aksrt'), $price_text); ?>
|
||
</button>
|
||
<?php endif; ?>
|
||
<p class="aksrt-paywall__hint">
|
||
<i class="fas fa-shield-alt"></i> <?php esc_html_e('购买后永久有效,支持重复查看', 'aksrt'); ?>
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<?php
|
||
return ob_get_clean();
|
||
}
|
||
}
|
||
|
||
// ============================================================
|
||
// 付费墙样式注入
|
||
// ============================================================
|
||
|
||
add_action('wp_head', function (): void {
|
||
if (is_admin() || !is_singular()) return;
|
||
global $post;
|
||
if (!($post instanceof WP_Post)) return;
|
||
if (!function_exists('aksrt_can_access_content') || aksrt_can_access_content($post)) return;
|
||
?>
|
||
<style>
|
||
.aksrt-paywall{position:relative;margin:24px 0}
|
||
.aksrt-paywall__overlay{position:relative}
|
||
.aksrt-paywall__blur{filter:blur(12px);pointer-events:none;user-select:none;max-height:200px;overflow:hidden;opacity:.3}
|
||
.aksrt-paywall__card{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);background:var(--surface-1,#fff);border:1px solid var(--color-line,#e5e7eb);border-radius:var(--radius-lg,14px);padding:40px;text-align:center;box-shadow:0 12px 40px rgba(0,0,0,.12);min-width:320px;max-width:420px}
|
||
.aksrt-paywall__icon{font-size:48px;color:var(--color-primary,#f04494);margin-bottom:16px}
|
||
.aksrt-paywall__title{font-size:20px;font-weight:700;margin:0 0 8px;color:var(--color-ink,#15171A)}
|
||
.aksrt-paywall__desc{font-size:14px;color:var(--color-ink-3,#525866);margin:0 0 20px}
|
||
.aksrt-paywall__price{margin-bottom:20px}
|
||
.aksrt-paywall__price-amount{font-size:28px;font-weight:700;color:var(--color-error,#EF4444)}
|
||
.aksrt-paywall__price-original{font-size:14px;color:var(--color-ink-4,#8A8F98);text-decoration:line-through;margin-left:8px}
|
||
.aksrt-paywall__price-free{font-size:20px;font-weight:600;color:var(--color-success,#10B981)}
|
||
.aksrt-paywall__btn{display:inline-flex;align-items:center;gap:8px}
|
||
.aksrt-paywall__hint{font-size:12px;color:var(--color-ink-4,#8A8F98);margin:16px 0 0}
|
||
.aksrt-gallery-locked{filter:blur(8px);pointer-events:none;user-select:none}
|
||
</style>
|
||
<?php
|
||
});
|
||
|
||
// ============================================================
|
||
// 付费图库轮播(Swiper)
|
||
// ============================================================
|
||
|
||
if (!function_exists('aksrt_render_paid_gallery')) {
|
||
function aksrt_render_paid_gallery(WP_Post $post): string {
|
||
$meta = aksrt_get_pay_meta($post);
|
||
if ($meta['pay_type'] !== AKSRT_PAY_TYPE_GALLERY) return '';
|
||
|
||
$images = $meta['gallery'] ?? [];
|
||
if (empty($images)) return '';
|
||
|
||
$has_access = aksrt_has_paid_access($post);
|
||
$base_uri = get_template_directory_uri();
|
||
|
||
ob_start();
|
||
?>
|
||
<div class="aksrt-paid-gallery" data-post-id="<?php echo esc_attr((string) $post->ID); ?>">
|
||
<?php if ($has_access) : ?>
|
||
<!-- 已付费:Swiper 轮播 -->
|
||
<div class="swiper aksrt-paid-gallery__swiper">
|
||
<div class="swiper-wrapper">
|
||
<?php foreach ($images as $img_url) : ?>
|
||
<div class="swiper-slide">
|
||
<img src="<?php echo esc_url($img_url); ?>" alt="" loading="lazy">
|
||
</div>
|
||
<?php endforeach; ?>
|
||
</div>
|
||
<div class="swiper-pagination"></div>
|
||
<div class="swiper-button-prev"></div>
|
||
<div class="swiper-button-next"></div>
|
||
</div>
|
||
<?php else : ?>
|
||
<!-- 未付费:模糊预览 + 付费墙 -->
|
||
<div class="aksrt-paid-gallery__locked">
|
||
<div class="aksrt-paid-gallery__preview">
|
||
<img src="<?php echo esc_url($images[0]); ?>" alt="" class="aksrt-gallery-locked">
|
||
</div>
|
||
<div class="aksrt-paid-gallery__overlay">
|
||
<i class="fas fa-images" aria-hidden="true"></i>
|
||
<p><?php esc_html_e('付费后即可查看全部图片', 'aksrt'); ?></p>
|
||
<?php
|
||
$price_label = aksrt_get_pay_price_label($post);
|
||
$currency = class_exists('AKSRT_Settings') ? AKSRT_Settings::get_currency_name() : __('积分', 'aksrt');
|
||
?>
|
||
<span class="aksrt-paid-gallery__price">
|
||
<?php echo esc_html($price_label['text']); ?>
|
||
</span>
|
||
</div>
|
||
</div>
|
||
<?php endif; ?>
|
||
</div>
|
||
|
||
<?php if ($has_access) : ?>
|
||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.css">
|
||
<script src="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.js"></script>
|
||
<script>
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
new Swiper('.aksrt-paid-gallery__swiper', {
|
||
loop: true,
|
||
pagination: { el: '.swiper-pagination', clickable: true },
|
||
navigation: { nextEl: '.swiper-button-next', prevEl: '.swiper-button-prev' },
|
||
lazy: true,
|
||
});
|
||
});
|
||
</script>
|
||
<?php endif; ?>
|
||
<?php
|
||
return ob_get_clean();
|
||
}
|
||
}
|
||
|
||
// ============================================================
|
||
// 付费视频播放器
|
||
// ============================================================
|
||
|
||
if (!function_exists('aksrt_render_paid_video')) {
|
||
function aksrt_render_paid_video(WP_Post $post): string {
|
||
$meta = aksrt_get_pay_meta($post);
|
||
if ($meta['pay_type'] !== AKSRT_PAY_TYPE_VIDEO) return '';
|
||
|
||
$video_url = $meta['video_url'] ?? '';
|
||
if (empty($video_url)) return '';
|
||
|
||
$has_access = aksrt_has_paid_access($post);
|
||
|
||
ob_start();
|
||
?>
|
||
<div class="aksrt-paid-video" data-post-id="<?php echo esc_attr((string) $post->ID); ?>">
|
||
<?php if ($has_access) : ?>
|
||
<!-- 已付费:视频播放器 -->
|
||
<div class="aksrt-paid-video__player">
|
||
<?php if (preg_match('/\.(mp4|webm|ogg)$/i', $video_url)) : ?>
|
||
<video controls preload="metadata" style="width:100%;border-radius:var(--radius-lg);">
|
||
<source src="<?php echo esc_url($video_url); ?>">
|
||
<?php esc_html_e('您的浏览器不支持视频播放。', 'aksrt'); ?>
|
||
</video>
|
||
<?php else : ?>
|
||
<div class="aksrt-paid-video__embed">
|
||
<iframe src="<?php echo esc_url($video_url); ?>" frameborder="0" allowfullscreen></iframe>
|
||
</div>
|
||
<?php endif; ?>
|
||
</div>
|
||
<?php else : ?>
|
||
<!-- 未付费:锁定预览 -->
|
||
<div class="aksrt-paid-video__locked">
|
||
<div class="aksrt-paid-video__placeholder">
|
||
<i class="fas fa-play-circle" aria-hidden="true"></i>
|
||
<p><?php esc_html_e('付费后即可观看完整视频', 'aksrt'); ?></p>
|
||
<?php $price_label = aksrt_get_pay_price_label($post); ?>
|
||
<span class="aksrt-paid-video__price"><?php echo esc_html($price_label['text']); ?></span>
|
||
</div>
|
||
</div>
|
||
<?php endif; ?>
|
||
</div>
|
||
<?php
|
||
return ob_get_clean();
|
||
}
|
||
}
|
||
|
||
// ============================================================
|
||
// 内容过滤钩子(注册在 core-hooks.php)
|
||
// ============================================================
|
||
|
||
// 这些函数供 core-hooks.php 中的 the_content 过滤器调用
|
||
if (!function_exists('aksrt_paid_content_filter')) {
|
||
function aksrt_paid_content_filter(string $content): string {
|
||
if (is_admin() || !is_singular()) return $content;
|
||
global $post;
|
||
if (!($post instanceof WP_Post)) return $content;
|
||
if (aksrt_can_access_content($post)) return $content;
|
||
|
||
// 图库类型:在内容末尾追加图库轮播
|
||
$meta = aksrt_get_pay_meta($post);
|
||
if ($meta['pay_type'] === AKSRT_PAY_TYPE_GALLERY) {
|
||
return aksrt_get_paywall_html($post) . aksrt_render_paid_gallery($post);
|
||
}
|
||
|
||
// 视频类型:在内容末尾追加视频播放器
|
||
if ($meta['pay_type'] === AKSRT_PAY_TYPE_VIDEO) {
|
||
return aksrt_get_paywall_html($post) . aksrt_render_paid_video($post);
|
||
}
|
||
|
||
return aksrt_get_paywall_html($post);
|
||
}
|
||
}
|
||
|
||
// 已付费的内容增强
|
||
if (!function_exists('aksrt_paid_content_enhance')) {
|
||
function aksrt_paid_content_enhance(string $content): string {
|
||
if (is_admin() || !is_singular()) return $content;
|
||
global $post;
|
||
if (!($post instanceof WP_Post)) return $content;
|
||
|
||
$meta = aksrt_get_pay_meta($post);
|
||
if ($meta['pay_type'] === AKSRT_PAY_TYPE_NO) return $content;
|
||
if (!aksrt_has_paid_access($post)) return $content;
|
||
|
||
// 已付费的图库:追加 Swiper 轮播
|
||
if ($meta['pay_type'] === AKSRT_PAY_TYPE_GALLERY) {
|
||
$gallery_html = aksrt_render_paid_gallery($post);
|
||
if ($gallery_html) $content .= $gallery_html;
|
||
}
|
||
|
||
// 已付费的视频:追加播放器
|
||
if ($meta['pay_type'] === AKSRT_PAY_TYPE_VIDEO) {
|
||
$video_html = aksrt_render_paid_video($post);
|
||
if ($video_html) $content .= $video_html;
|
||
}
|
||
|
||
return $content;
|
||
}
|
||
}
|
||
|
||
// ============================================================
|
||
// 付费图库/视频样式
|
||
// ============================================================
|
||
|
||
add_action('wp_head', function (): void {
|
||
if (is_admin() || !is_singular()) return;
|
||
global $post;
|
||
if (!($post instanceof WP_Post)) return;
|
||
|
||
$meta = aksrt_get_pay_meta($post);
|
||
if (!in_array($meta['pay_type'], [AKSRT_PAY_TYPE_GALLERY, AKSRT_PAY_TYPE_VIDEO], true)) return;
|
||
?>
|
||
<style>
|
||
.aksrt-paid-gallery{margin:24px 0}
|
||
.aksrt-paid-gallery__swiper{border-radius:var(--radius-lg);overflow:hidden}
|
||
.aksrt-paid-gallery__swiper img{width:100%;height:auto;display:block}
|
||
.aksrt-paid-gallery__locked{position:relative;border-radius:var(--radius-lg);overflow:hidden}
|
||
.aksrt-paid-gallery__preview img{width:100%;display:block}
|
||
.aksrt-paid-gallery__overlay{position:absolute;inset:0;background:rgba(0,0,0,.5);display:flex;flex-direction:column;align-items:center;justify-content:center;color:#fff;gap:12px}
|
||
.aksrt-paid-gallery__overlay i{font-size:48px;opacity:.8}
|
||
.aksrt-paid-gallery__overlay p{font-size:16px;margin:0}
|
||
.aksrt-paid-gallery__price{font-size:20px;font-weight:700;background:rgba(255,255,255,.2);padding:6px 16px;border-radius:20px}
|
||
|
||
.aksrt-paid-video{margin:24px 0}
|
||
.aksrt-paid-video__embed{position:relative;padding-bottom:56.25%;height:0;overflow:hidden;border-radius:var(--radius-lg)}
|
||
.aksrt-paid-video__embed iframe{position:absolute;top:0;left:0;width:100%;height:100%;border:none}
|
||
.aksrt-paid-video__locked{background:var(--surface-2);border-radius:var(--radius-lg);padding:60px 20px;text-align:center}
|
||
.aksrt-paid-video__placeholder i{font-size:64px;color:var(--color-ink-4);margin-bottom:16px}
|
||
.aksrt-paid-video__placeholder p{font-size:16px;color:var(--color-ink-3);margin:0 0 12px}
|
||
.aksrt-paid-video__price{font-size:20px;font-weight:700;color:var(--color-error)}
|
||
</style>
|
||
<?php
|
||
});
|