1431 lines
61 KiB
PHP
1431 lines
61 KiB
PHP
<?php
|
||
/**
|
||
* AKSRT Pay System — 统一付费内容系统(1:1 对齐 legacy)
|
||
*
|
||
* 核心机制:
|
||
* - 任何 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\Core\DomainModel::PAY_TYPE_NO);
|
||
}
|
||
if (!defined('AKSRT_PAY_TYPE_READING')) {
|
||
define('AKSRT_PAY_TYPE_READING', \AKSRT\Core\DomainModel::PAY_TYPE_READING);
|
||
}
|
||
if (!defined('AKSRT_PAY_TYPE_DOWNLOAD')) {
|
||
define('AKSRT_PAY_TYPE_DOWNLOAD', \AKSRT\Core\DomainModel::PAY_TYPE_DOWNLOAD);
|
||
}
|
||
if (!defined('AKSRT_PAY_TYPE_GALLERY')) {
|
||
define('AKSRT_PAY_TYPE_GALLERY', \AKSRT\Core\DomainModel::PAY_TYPE_GALLERY);
|
||
}
|
||
if (!defined('AKSRT_PAY_TYPE_VIDEO')) {
|
||
define('AKSRT_PAY_TYPE_VIDEO', \AKSRT\Core\DomainModel::PAY_TYPE_VIDEO);
|
||
}
|
||
if (!defined('AKSRT_PAY_TYPE_VIP')) {
|
||
define('AKSRT_PAY_TYPE_VIP', \AKSRT\Core\DomainModel::PAY_TYPE_VIP);
|
||
}
|
||
|
||
if (!function_exists('aksrt_asset_price_minor')) {
|
||
function aksrt_asset_price_minor($value, string $pay_modo): int {
|
||
if ($pay_modo === 'points') {
|
||
return is_int($value) ? max(0, $value) : 0;
|
||
}
|
||
if (!is_string($value)) {
|
||
return 0;
|
||
}
|
||
try {
|
||
return \AKSRT\Ledger\AssetAmount::balance_minor($value);
|
||
} catch (\InvalidArgumentException $e) {
|
||
return 0;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (!function_exists('aksrt_asset_price_value')) {
|
||
function aksrt_asset_price_value(int $minor, string $pay_modo) {
|
||
return $pay_modo === 'points'
|
||
? max(0, $minor)
|
||
: \AKSRT\Ledger\AssetAmount::decimal_from_minor(max(0, $minor));
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 检查用户是否为 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\Core\DomainModel::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'),
|
||
];
|
||
}
|
||
|
||
/**
|
||
* Cash payment methods available for content cashiers.
|
||
*
|
||
* @return array<int, array{id:string,name:string}>
|
||
*/
|
||
if (!function_exists('aksrt_list_cash_payment_methods')) {
|
||
function aksrt_list_cash_payment_methods(bool $logged_in = true): array {
|
||
$ids = $logged_in
|
||
? ['balance', 'alipay', 'wechatpay', 'paypal', 'stripe', 'epay_alipay', 'epay_wxpay']
|
||
: ['alipay', 'wechatpay', 'paypal', 'stripe', 'epay_alipay', 'epay_wxpay'];
|
||
|
||
if (!class_exists('\AKSRT\Payment\Gateway')) {
|
||
// No gateway manager → no real methods (avoid dead balance/alipay stubs).
|
||
return [];
|
||
}
|
||
|
||
$methods = [];
|
||
$gw = \AKSRT\Payment\Gateway::get_instance();
|
||
foreach ($ids as $id) {
|
||
$g = $gw->get_gateway($id);
|
||
if (!$g) {
|
||
continue;
|
||
}
|
||
if (method_exists($g, 'is_configured') && !$g->is_configured()) {
|
||
continue;
|
||
}
|
||
$methods[] = [
|
||
'id' => $id,
|
||
'name' => method_exists($g, 'get_name') ? (string) $g->get_name() : $id,
|
||
];
|
||
}
|
||
|
||
// Do not inject a fake balance/alipay option when nothing is configured —
|
||
// callers should surface “暂无可用支付方式” instead of a dead method.
|
||
return $methods;
|
||
}
|
||
}
|
||
|
||
// ============================================================
|
||
// 元数据读取
|
||
// ============================================================
|
||
|
||
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\Core\DomainModel::PAY_META_KEY, true);
|
||
if (is_array($meta) && isset($meta['pay_type'])) {
|
||
return wp_parse_args(\AKSRT\Core\DomainModel::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\Core\DomainModel::default_pay_meta();
|
||
}
|
||
}
|
||
|
||
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\Core\DomainModel::RESOURCE_DOWNLOAD_COUNT_KEY, true);
|
||
$view_count = (int) get_post_meta($p->ID, \AKSRT\Core\DomainModel::POST_VIEW_COUNT_KEY, true);
|
||
$virtual_sales = max(0, (int) ($pay_meta['virtual_sales'] ?? 0));
|
||
$sales_volume = (int) get_post_meta($p->ID, \AKSRT\Core\DomainModel::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 = $pay_meta['download_limit_over_price'] ?? ($pay_modo === 'points' ? 0 : '0.00');
|
||
|
||
return [
|
||
'version' => (string) ($pay_meta['version'] ?? ''),
|
||
'size' => (string) ($pay_meta['size'] ?? ''),
|
||
'price' => (string) ($pay_meta['price'] ?? '0.00'),
|
||
'points_price' => (int) ($pay_meta['points_price'] ?? 0),
|
||
'pay_modo' => $pay_modo,
|
||
'pay_limit' => $pay_limit,
|
||
'original_price' => (string) ($pay_meta['original_price'] ?? '0.00'),
|
||
'vip1_price' => (string) ($pay_meta['vip1_price'] ?? '0.00'),
|
||
'vip2_price' => (string) ($pay_meta['vip2_price'] ?? '0.00'),
|
||
'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),
|
||
// legacy: 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\Payment\OrderRepository')) {
|
||
return null;
|
||
}
|
||
$orders = \AKSRT\Payment\OrderRepository::get_instance()->get_user_orders($uid, [
|
||
'product_type' => \AKSRT\Core\DomainModel::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\Core\DomainModel::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\Core\DomainModel::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 (legacy 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\Settings') ? \AKSRT\Settings\Settings::get_order_settings() : [];
|
||
$paid = class_exists('\AKSRT\Settings\Settings') ? \AKSRT\Settings\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 (legacy 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\Settings')) {
|
||
$order = \AKSRT\Settings\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 (legacy-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((string) ($order['amount'] ?? '0.00'))
|
||
: (string) ($order['amount'] ?? '0.00');
|
||
return $id . '_' . $post_id . '_' . trim($num) . '_' . $amount . '_' . trim($ip);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Set guest paid cookies after cash order paid (content).
|
||
*
|
||
* @param array<string,mixed> $order
|
||
*/
|
||
if (!function_exists('aksrt_set_guest_pay_cookies')) {
|
||
function aksrt_set_guest_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;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Set guest paid cookies after content order paid (legacy aksrtpay_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 {
|
||
aksrt_set_guest_pay_cookies($order);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Resolve guest paid order from browser cookie for a product.
|
||
*
|
||
* @return array<string,mixed>|null
|
||
*/
|
||
if (!function_exists('aksrt_get_guest_paid_order')) {
|
||
function aksrt_get_guest_paid_order(int $product_id, string $product_type = ''): ?array {
|
||
if ($product_id <= 0 || empty($_COOKIE['aksrt_pay_' . $product_id])) {
|
||
return null;
|
||
}
|
||
if (!class_exists('\AKSRT\Payment\OrderRepository') || !class_exists('\AKSRT\Core\DomainModel')) {
|
||
return null;
|
||
}
|
||
$order_number = sanitize_text_field((string) $_COOKIE['aksrt_pay_' . $product_id]);
|
||
if ($order_number === '') {
|
||
return null;
|
||
}
|
||
$order = \AKSRT\Payment\OrderRepository::get_instance()->get($order_number);
|
||
if (!$order) {
|
||
return null;
|
||
}
|
||
$ptype = sanitize_key((string) ($order['product_type'] ?? ''));
|
||
$want = sanitize_key($product_type);
|
||
if ($want !== '' && $ptype !== $want) {
|
||
return null;
|
||
}
|
||
if ((int) ($order['product_id'] ?? 0) !== $product_id) {
|
||
return null;
|
||
}
|
||
// Guest orders only (logged-in users use user_id access).
|
||
if ((int) ($order['user_id'] ?? -1) !== 0) {
|
||
return null;
|
||
}
|
||
$status = (string) ($order['status'] ?? '');
|
||
if (!\AKSRT\Core\DomainModel::is_paid_order_status($status)) {
|
||
return null;
|
||
}
|
||
|
||
$paid_at = (string) ($order['paid_at'] ?? $order['created_at'] ?? '');
|
||
if ($paid_at !== '') {
|
||
$ts = strtotime($paid_at);
|
||
$days = aksrt_guest_pay_cookie_days($product_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;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Resolve guest paid order from browser cookie for a post (content/resource).
|
||
*
|
||
* @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 {
|
||
$type = class_exists('\AKSRT\Core\DomainModel')
|
||
? \AKSRT\Core\DomainModel::ORDER_PRODUCT_RESOURCE
|
||
: 'resource';
|
||
return aksrt_get_guest_paid_order($post_id, $type);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Whether current request has paid access via guest cookie (content/resource product).
|
||
*/
|
||
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 (legacy 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:int|string,base:int|string,vip_amount:int|string|null,modo:string,currency:string,is_free:bool,pay_limit:int,vip_level:int,original:int|string,vip1:int|string,vip2:int|string}
|
||
*/
|
||
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\Core\DomainModel::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\Settings') ? \AKSRT\Settings\Settings::get_currency_name() : __('积分', 'aksrt');
|
||
$cash_symbol = '¥';
|
||
if (class_exists('\AKSRT\Settings\Settings')) {
|
||
$paid = \AKSRT\Settings\Settings::get_paid_content_settings();
|
||
$cash_symbol = (string) ($paid['display']['currency_symbol'] ?? '¥');
|
||
}
|
||
|
||
if ($pay_modo === 'points') {
|
||
$base_minor = max(0, (int) ($meta['points_price'] ?? 0));
|
||
$vip1_minor = max(0, (int) ($meta['vip1_points'] ?? 0));
|
||
$vip2_minor = max(0, (int) ($meta['vip2_points'] ?? 0));
|
||
$currency_label = $currency;
|
||
} else {
|
||
$parse_cash = static function ($value): int {
|
||
if (!is_string($value)) {
|
||
return 0;
|
||
}
|
||
try {
|
||
return \AKSRT\Ledger\AssetAmount::balance_minor($value);
|
||
} catch (\InvalidArgumentException $e) {
|
||
return 0;
|
||
}
|
||
};
|
||
$base_minor = $parse_cash($meta['price'] ?? '0');
|
||
$vip1_minor = $parse_cash($meta['vip1_price'] ?? '0');
|
||
$vip2_minor = $parse_cash($meta['vip2_price'] ?? '0');
|
||
$currency_label = $cash_symbol;
|
||
}
|
||
|
||
// Positive VIP tier prices only (0 defaults mean "not configured").
|
||
$vip_minor = null;
|
||
if ($vip_level >= 2 && $vip2_minor > 0) {
|
||
$vip_minor = $vip2_minor;
|
||
} elseif ($vip_level >= 1 && $vip1_minor > 0) {
|
||
$vip_minor = $vip1_minor;
|
||
}
|
||
|
||
$amount_minor = $base_minor;
|
||
if ($vip_minor !== null && ($base_minor <= 0 || $vip_minor < $base_minor)) {
|
||
$amount_minor = $vip_minor;
|
||
}
|
||
|
||
// Role/VIP free-download exemption (global setting).
|
||
if ($amount_minor > 0 && $uid > 0 && function_exists('aksrt_is_download_credit_exempt_user') && aksrt_is_download_credit_exempt_user($uid)) {
|
||
$amount_minor = 0;
|
||
}
|
||
|
||
if ($pay_modo === 'points') {
|
||
$original = max(0, (int) ($meta['original_price'] ?? 0));
|
||
$amount = $amount_minor;
|
||
$base = $base_minor;
|
||
$vip1 = $vip1_minor;
|
||
$vip2 = $vip2_minor;
|
||
$vip_amount = $vip_minor;
|
||
} else {
|
||
$original_minor = $parse_cash($meta['original_price'] ?? '0');
|
||
$amount = \AKSRT\Ledger\AssetAmount::decimal_from_minor($amount_minor);
|
||
$base = \AKSRT\Ledger\AssetAmount::decimal_from_minor($base_minor);
|
||
$vip1 = \AKSRT\Ledger\AssetAmount::decimal_from_minor($vip1_minor);
|
||
$vip2 = \AKSRT\Ledger\AssetAmount::decimal_from_minor($vip2_minor);
|
||
$vip_amount = $vip_minor === null ? null : \AKSRT\Ledger\AssetAmount::decimal_from_minor($vip_minor);
|
||
$original = \AKSRT\Ledger\AssetAmount::decimal_from_minor($original_minor);
|
||
}
|
||
|
||
return [
|
||
'amount' => $amount,
|
||
'base' => $base,
|
||
'vip_amount' => $vip_amount,
|
||
'modo' => $pay_modo,
|
||
'currency' => $currency_label,
|
||
'is_free' => $amount_minor <= 0,
|
||
'pay_limit' => $pay_limit,
|
||
'vip_level' => $vip_level,
|
||
'original' => $original,
|
||
'vip1' => $vip1,
|
||
'vip2' => $vip2,
|
||
];
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Whether content cashier may accept coupons for this post (legacy coupon_post_s || coupon_s).
|
||
*/
|
||
if (!function_exists('aksrt_is_content_coupon_allowed')) {
|
||
function aksrt_is_content_coupon_allowed($post = null): bool {
|
||
$meta = function_exists('aksrt_get_pay_meta') ? aksrt_get_pay_meta($post) : [];
|
||
if (!empty($meta['coupon_s'])) {
|
||
return true;
|
||
}
|
||
if (class_exists('\AKSRT\Settings\Settings') && method_exists('\AKSRT\Settings\Settings', 'get_content_coupon_settings')) {
|
||
$cfg = \AKSRT\Settings\Settings::get_content_coupon_settings();
|
||
return !empty($cfg['enabled']);
|
||
}
|
||
if (class_exists('\AKSRT\Settings\Settings') && method_exists('\AKSRT\Settings\Settings', 'get_paid_content_settings')) {
|
||
$paid = \AKSRT\Settings\Settings::get_paid_content_settings();
|
||
return !empty($paid['coupon_post_s']);
|
||
}
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Buyer-facing 推广折扣 amount for a post (0 if no eligible referrer).
|
||
* Cash only; mirrors aksrtpay_get_post_rebate_discount.
|
||
*/
|
||
if (!function_exists('aksrt_get_post_rebate_discount')) {
|
||
function aksrt_get_post_rebate_discount($post = null, int $user_id = 0): string {
|
||
$meta = function_exists('aksrt_get_pay_meta') ? aksrt_get_pay_meta($post) : [];
|
||
$fixed = is_string($meta['pay_rebate_discount'] ?? null) ? (string) $meta['pay_rebate_discount'] : '0.00';
|
||
$fixed_minor = aksrt_asset_price_minor($fixed, '0');
|
||
if ($fixed_minor <= 0) {
|
||
return '0.00';
|
||
}
|
||
if (!class_exists('\AKSRT\Settings\Settings') || !method_exists('\AKSRT\Settings\Settings', 'get_payment_affiliate_settings')) {
|
||
return '0.00';
|
||
}
|
||
$aff = \AKSRT\Settings\Settings::get_payment_affiliate_settings();
|
||
if (empty($aff['enabled'])) {
|
||
return '0.00';
|
||
}
|
||
$uid = $user_id > 0 ? $user_id : (function_exists('get_current_user_id') ? (int) get_current_user_id() : 0);
|
||
$referrer_id = class_exists('\AKSRT\Payment\ReferralAttribution')
|
||
? \AKSRT\Payment\ReferralAttribution::get_instance()->resolve($uid)
|
||
: 0;
|
||
if ($referrer_id <= 0) {
|
||
return '0.00';
|
||
}
|
||
// Need a positive ratio for referrer (VIP tier of referrer preferred).
|
||
$ratio = (int) \AKSRT\Settings\Settings::affiliate_ratio_for_user($referrer_id);
|
||
if ($ratio <= 0) {
|
||
return '0.00';
|
||
}
|
||
return \AKSRT\Ledger\AssetAmount::decimal_from_minor($fixed_minor);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Whether cash checkout may use points deduction (legacy points_deduction).
|
||
*
|
||
* legacy: aksrtpay_is_allow_points_deduction() always returns false ("暂未启用").
|
||
* Keep the same contract: schema/UI may exist, runtime is hard-off unless filter overrides.
|
||
*/
|
||
if (!function_exists('aksrt_is_allow_points_deduction')) {
|
||
function aksrt_is_allow_points_deduction(string $context = 'content'): bool {
|
||
// Mirror legacy hard-off first.
|
||
$allowed = false;
|
||
if (class_exists('\AKSRT\Settings\Settings') && method_exists('\AKSRT\Settings\Settings', 'get_all')) {
|
||
$all = \AKSRT\Settings\Settings::get_all();
|
||
// Even if switch on, default remains false like legacy until product enables it.
|
||
if (!empty($all['points_deduction_s']) && class_exists('\AKSRT\Settings\Settings') && method_exists('\AKSRT\Settings\Settings', 'credits_enabled') && \AKSRT\Settings\Settings::credits_enabled()) {
|
||
$allowed = false; // still disabled at runtime
|
||
}
|
||
}
|
||
return (bool) apply_filters('aksrt_is_allow_points_deduction', $allowed, $context);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Content cashier does not accept card_pass payment_method.
|
||
* Card-pass for balance/VIP/points is user-center redeem (legacy order types 4/8/9),
|
||
* not content pay types (reading/download/gallery/video).
|
||
*/
|
||
if (!function_exists('aksrt_is_content_card_pass_pay_allowed')) {
|
||
function aksrt_is_content_card_pass_pay_allowed($post = null): bool {
|
||
return (bool) apply_filters('aksrt_is_content_card_pass_pay_allowed', false, $post);
|
||
}
|
||
}
|
||
|
||
// ============================================================
|
||
// 状态判断
|
||
// ============================================================
|
||
|
||
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;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Detect the canonical paid-fragment shortcode in a post's raw content.
|
||
*
|
||
* The editor writes this exact shortcode format, so this is the single
|
||
* source of truth for whether a reading price has any protected fragment to
|
||
* apply to. It deliberately does not inspect rendered content.
|
||
*/
|
||
if (!function_exists('aksrt_post_has_payshow_shortcode')) {
|
||
function aksrt_post_has_payshow_shortcode($post = null): bool {
|
||
$p = get_post($post);
|
||
if (!($p instanceof WP_Post)) {
|
||
return false;
|
||
}
|
||
|
||
return preg_match(
|
||
'/\[hidecontent\b[^\]]*\btype\s*=\s*(?:"payshow"|\'payshow\'|payshow)[^\]]*\]/i',
|
||
(string) $p->post_content
|
||
) === 1;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* A paid fragment is active only for an explicitly priced paid-reading post.
|
||
*
|
||
* A zero-price reading configuration is intentionally public: authors can
|
||
* save content before setting a price without accidentally hiding it.
|
||
*/
|
||
if (!function_exists('aksrt_is_payshow_purchase_enabled')) {
|
||
function aksrt_is_payshow_purchase_enabled($post = null): bool {
|
||
if (!aksrt_post_has_payshow_shortcode($post)) {
|
||
return false;
|
||
}
|
||
|
||
$meta = aksrt_get_pay_meta($post);
|
||
if (($meta['pay_type'] ?? AKSRT_PAY_TYPE_NO) !== AKSRT_PAY_TYPE_READING) {
|
||
return false;
|
||
}
|
||
|
||
$pay_modo = ($meta['pay_modo'] ?? '0') === 'points' ? 'points' : '0';
|
||
$price = $pay_modo === 'points'
|
||
? (int) ($meta['points_price'] ?? 0)
|
||
: (string) ($meta['price'] ?? '0.00');
|
||
|
||
return aksrt_asset_price_minor($price, $pay_modo) > 0;
|
||
}
|
||
}
|
||
|
||
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;
|
||
$pay_modo = ($meta['pay_modo'] ?? '0') === 'points' ? 'points' : '0';
|
||
$price = $pay_modo === 'points' ? (int) ($meta['points_price'] ?? 0) : (string) ($meta['price'] ?? '0.00');
|
||
if (aksrt_asset_price_minor($price, $pay_modo) <= 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()。
|
||
* 不再使用 download_credit 做按次扣积分门禁。
|
||
*
|
||
* @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'));
|
||
}
|
||
|
||
$uid = $user_id > 0 ? $user_id : get_current_user_id();
|
||
if (!function_exists('aksrt_user_can') || !aksrt_user_can('resource_download', $uid, (int) $p->ID)) {
|
||
$login_url = wp_login_url(get_permalink($p));
|
||
return new WP_Error('aksrt_need_login', __('请先登录。', 'aksrt'), ['login_url' => $login_url]);
|
||
}
|
||
|
||
if ($uid > 0 && function_exists('aksrt_has_paid_access') && aksrt_has_paid_access($p, $uid)) {
|
||
return true;
|
||
}
|
||
if ($uid > 0 && function_exists('aksrt_is_vip_exempt') && aksrt_is_vip_exempt($uid)) {
|
||
return true;
|
||
}
|
||
|
||
// Fallback without Content_Visibility: require purchase when priced.
|
||
$cost = function_exists('aksrt_get_download_cost') ? aksrt_get_download_cost($p) : 0;
|
||
if ($cost > 0) {
|
||
return new WP_Error('aksrt_need_purchase', __('请先购买后再下载。', 'aksrt'), [
|
||
'status' => 402,
|
||
'cost' => $cost,
|
||
'post_id'=> (int) $p->ID,
|
||
]);
|
||
}
|
||
|
||
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);
|
||
if (($meta['pay_modo'] ?? '0') !== 'points') {
|
||
return 0;
|
||
}
|
||
return max(0, (int) ($meta['points_price'] ?? 0));
|
||
}
|
||
}
|
||
|
||
/**
|
||
* URL of the legacy-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 (legacy 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\Settings')) {
|
||
return false;
|
||
}
|
||
$paid = \AKSRT\Settings\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\Settings') ? \AKSRT\Settings\Settings::get_currency_name() : __('积分', 'aksrt');
|
||
$cash_symbol = '¥';
|
||
if (class_exists('\AKSRT\Settings\Settings')) {
|
||
$paid = \AKSRT\Settings\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) ? ($priced['base'] ?? ($pay_modo === 'points' ? 0 : '0.00')) : ($pay_modo === 'points' ? 0 : '0.00'),
|
||
'modo' => $pay_modo,
|
||
'pay_limit' => $pay_limit,
|
||
];
|
||
}
|
||
|
||
$price = is_array($priced)
|
||
? ($priced['amount'] ?? ($pay_modo === 'points' ? 0 : '0.00'))
|
||
: ($pay_modo === 'points' ? (int) ($meta['points_price'] ?? 0) : (string) ($meta['price'] ?? '0.00'));
|
||
$base = is_array($priced) ? ($priced['base'] ?? $price) : $price;
|
||
$price_minor = aksrt_asset_price_minor($price, $pay_modo);
|
||
|
||
if ($price_minor <= 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($price): string {
|
||
if (is_int($price)) {
|
||
return (string) max(0, $price);
|
||
}
|
||
if (!is_string($price)) {
|
||
return '0';
|
||
}
|
||
try {
|
||
$decimal = \AKSRT\Ledger\AssetAmount::balance($price)->decimal();
|
||
} catch (\InvalidArgumentException $e) {
|
||
return '0';
|
||
}
|
||
return str_ends_with($decimal, '.00')
|
||
? substr($decimal, 0, -3)
|
||
: rtrim($decimal, '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-image'],
|
||
AKSRT_PAY_TYPE_VIDEO => ['label' => __('付费视频', 'aksrt'), 'color' => '#8B5CF6', 'icon' => 'fa-play-circle'],
|
||
AKSRT_PAY_TYPE_VIP => ['label' => __('VIP 专属', 'aksrt'), 'color' => '#3B82F6', 'icon' => 'fa-certificate'],
|
||
];
|
||
|
||
if (!isset($badges[$pay_type])) return '';
|
||
$b = $badges[$pay_type];
|
||
return sprintf(
|
||
'<span class="aksrt-pay-badge" style="background:%s"><i class="fa %s"></i> %s</span>',
|
||
esc_attr($b['color']), esc_attr($b['icon']), esc_html($b['label'])
|
||
);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* legacy aksrtpay_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\Settings')) {
|
||
$order = \AKSRT\Settings\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'] ?? 'fa fas fa-check-circle'));
|
||
if ($icon === '') {
|
||
$icon = 'fa 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 (legacy aksrtpay_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="fa 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 (legacy aksrtpay_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, array $args = []): string {
|
||
$is_widget = ($args['layout'] ?? 'article') === 'widget';
|
||
$theme = sanitize_html_class((string) ($args['theme'] ?? 'jb-red')) ?: 'jb-red';
|
||
$meta = aksrt_get_pay_meta($post);
|
||
$pay_type = (string) ($meta['pay_type'] ?? AKSRT_PAY_TYPE_NO);
|
||
if ($pay_type === AKSRT_PAY_TYPE_NO) return '';
|
||
|
||
$priced = aksrt_get_user_pay_price($post);
|
||
$modo = (string) ($priced['modo'] ?? '0');
|
||
$amount = $priced['amount'] ?? 0;
|
||
$amount_minor = aksrt_asset_price_minor($amount, $modo);
|
||
$price_label = aksrt_get_pay_price_label($post);
|
||
$price_text = (string) ($price_label['text'] ?? __('免费', 'aksrt'));
|
||
$pay_limit = (int) ($priced['pay_limit'] ?? ($meta['pay_limit'] ?? 0));
|
||
$vip_level = (int) ($priced['vip_level'] ?? 0);
|
||
$member_blocked = $pay_limit > 0 && $vip_level < $pay_limit;
|
||
$type_labels = [
|
||
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'),
|
||
];
|
||
$type_label = $type_labels[$pay_type] ?? __('付费内容', 'aksrt');
|
||
$order_types = [
|
||
AKSRT_PAY_TYPE_READING => 1,
|
||
AKSRT_PAY_TYPE_DOWNLOAD => 2,
|
||
AKSRT_PAY_TYPE_GALLERY => 5,
|
||
AKSRT_PAY_TYPE_VIDEO => 6,
|
||
AKSRT_PAY_TYPE_VIP => 4,
|
||
];
|
||
$order_type = (int) ($order_types[$pay_type] ?? 1);
|
||
$title = get_the_title($post);
|
||
$doc = trim((string) ($args['message'] ?? ''));
|
||
if ($doc === '') {
|
||
$doc = sprintf(__('此内容为%s,请付费后查看', 'aksrt'), $type_label);
|
||
}
|
||
$sales = max(0, (int) ($meta['virtual_sales'] ?? 0));
|
||
$sales += max(0, (int) get_post_meta($post->ID, \AKSRT\Core\DomainModel::RESOURCE_SALES_VOLUME_KEY, true));
|
||
$tag = $is_widget
|
||
? '<badge class="pay-tag abs-center">' . esc_html($type_label) . '</badge>'
|
||
: '<div class="pay-tag abs-center">' . esc_html($type_label) . '</div>';
|
||
$block_class = $is_widget ? ' btn-block' : '';
|
||
$button = '';
|
||
if (!is_user_logged_in()) {
|
||
if ($pay_limit <= 0 && $amount_minor > 0 && $modo === '0' && aksrt_allow_guest_content_pay($post)) {
|
||
$button = '<button type="button" class="but jb-red padding-lg cashier-link' . $block_class . '" data-aksrt-pay-content="' . esc_attr((string) $post->ID) . '"><i class="fa fa-shopping-cart" aria-hidden="true"></i> ' . esc_html__('立即购买', 'aksrt') . '</button>';
|
||
} else {
|
||
$button = '<a href="' . esc_url(wp_login_url(get_permalink($post))) . '" class="but jb-blue padding-lg' . $block_class . '"><i class="fa fa-sign-in" aria-hidden="true"></i> ' . esc_html__('登录购买', 'aksrt') . '</a>';
|
||
}
|
||
} elseif ($member_blocked) {
|
||
$vip_url = aksrt_get_user_center_tab_url('vip');
|
||
$button = '<a href="' . esc_url($vip_url) . '" class="but jb-vip' . max(1, min(2, $pay_limit)) . ' padding-lg' . $block_class . '"><i class="fa fa-certificate" aria-hidden="true"></i> ' . esc_html__('开通会员后可查看', 'aksrt') . '</a>';
|
||
} elseif ($amount_minor > 0) {
|
||
$button = '<button type="button" class="but ' . ($modo === 'points' ? 'jb-yellow' : 'jb-red') . ' padding-lg cashier-link' . $block_class . '" data-aksrt-pay-content="' . esc_attr((string) $post->ID) . '"><i class="fa fa-shopping-cart" aria-hidden="true"></i> ' . esc_html__('立即购买', 'aksrt') . '</button>';
|
||
} else {
|
||
$button = '<button type="button" class="but jb-blue padding-lg cashier-link' . $block_class . '" data-aksrt-pay-content="' . esc_attr((string) $post->ID) . '"><i class="fa fa-eye" aria-hidden="true"></i> ' . esc_html__('查看内容', 'aksrt') . '</button>';
|
||
}
|
||
$price_color = ($price_label['free'] ?? false) ? 'c-green' : ($modo === 'points' ? 'c-yellow' : 'c-red');
|
||
$price = '<div class="price-box"><div class="' . esc_attr($price_color) . '"><b class="em3x">' . esc_html($price_text) . '</b></div></div>';
|
||
$vip_items = [];
|
||
$base_minor = aksrt_asset_price_minor($priced['base'] ?? $amount, $modo);
|
||
foreach ([1, 2] as $level) {
|
||
$key = $modo === 'points' ? 'vip' . $level . '_points' : 'vip' . $level . '_price';
|
||
$value = $meta[$key] ?? 0;
|
||
$vip_minor = aksrt_asset_price_minor($value, $modo);
|
||
if ($vip_minor > 0 && ($base_minor <= 0 || $vip_minor < $base_minor)) {
|
||
$vip_items[] = '<span class="but vip-price"><span class="vip-price-text">' . esc_html(sprintf(__('VIP%d', 'aksrt'), $level)) . '</span><span class="em12 ml3">' . esc_html(aksrt_format_price($value)) . '</span></span>';
|
||
}
|
||
}
|
||
$vip = implode('', $vip_items);
|
||
if (count($vip_items) > 1) {
|
||
$vip = '<span class="vip-price-buts but-average inline">' . $vip . '</span>';
|
||
}
|
||
$attrs = aksrt_get_pay_attributes_html($post, 'pay-attr');
|
||
$count = $sales > 0 ? '<badge class="img-badge hot jb-blue px12">' . esc_html(sprintf(__('已售 %s', 'aksrt'), $sales)) . '</badge>' : '';
|
||
$service = aksrt_get_pay_service_html('muted-2-color');
|
||
$demo = aksrt_get_demo_link_html($post, 'but jb-yellow padding-lg');
|
||
$demo_row = $demo !== '' ? '<div class="mt10">' . $demo . '</div>' : '';
|
||
$discount = $modo === '0' ? aksrt_get_post_rebate_discount($post) : '0.00';
|
||
$discount_text = aksrt_asset_price_minor($discount, '0') > 0
|
||
? sprintf(__('推广优惠 %s', 'aksrt'), (string) ($priced['currency'] ?? '¥') . aksrt_format_price($discount))
|
||
: '';
|
||
$discount_banner = $discount_text !== '' ? '<div class="text-center padding-6 jb-yellow px12">' . esc_html($discount_text) . '</div>' : '';
|
||
|
||
if ($is_widget && aksrt_has_paid_access($post)) {
|
||
$view_labels = [
|
||
AKSRT_PAY_TYPE_DOWNLOAD => '<i class="fa fa-download fa-fw" aria-hidden="true"></i>' . esc_html__('获取下载地址', 'aksrt'),
|
||
AKSRT_PAY_TYPE_GALLERY => '<i class="fa fa-file-image-o fa-fw" aria-hidden="true"></i>' . esc_html__('查看图片', 'aksrt'),
|
||
AKSRT_PAY_TYPE_VIDEO => '<i class="fa fa-play-circle fa-fw" aria-hidden="true"></i>' . esc_html__('查看视频', 'aksrt'),
|
||
];
|
||
$view_label = $view_labels[$pay_type] ?? '<i class="fa fa-dot-circle-o fa-fw" aria-hidden="true"></i>' . esc_html__('查看内容', 'aksrt');
|
||
return '<div class="zib-widget pay-box pay-widget paid-box order-type-' . $order_type . '" style="padding:0">' . $tag . '<div class="relative-h ' . esc_attr($theme) . '" style="background-size:120%"><div class="pay-widget__orb pay-widget__orb--right ' . esc_attr($theme) . '"></div><div class="pay-widget__orb pay-widget__orb--left ' . esc_attr($theme) . '"></div><div class="relative box-body"><div class="text-center mt10 box-body"><p><i class="fa fa-shopping-bag fa-3x" aria-hidden="true"></i></p><b class="em14">' . esc_html__('已购买', 'aksrt') . '</b></div></div></div><div class="box-body"><div class="px12 muted-2-color">' . $service . '</div><div class="mt10"><a href="#posts-pay" class="but padding-lg btn-block ' . esc_attr($theme) . '">' . $view_label . '</a></div>' . $demo_row . $attrs . '</div>' . $count . '</div>';
|
||
}
|
||
|
||
if ($is_widget) {
|
||
return '<div class="zib-widget pay-box pay-widget order-type-' . $order_type . '" style="padding:0">' . $tag . '<div class="relative-h ' . esc_attr($theme) . '" style="background-size:120%"><div class="pay-widget__orb pay-widget__orb--right ' . esc_attr($theme) . '"></div><div class="pay-widget__orb pay-widget__orb--left ' . esc_attr($theme) . '"></div><div class="relative box-body"><div class="text-center mt10">' . $price . '</div></div><div class="relative">' . $vip . '</div></div>' . $discount_banner . '<div class="box-body"><div class="px12 muted-2-color">' . $service . '</div><div class="mt10">' . $button . '</div>' . $demo_row . $attrs . '</div>' . $count . '</div>';
|
||
}
|
||
|
||
$thumb = get_the_post_thumbnail($post, 'medium', ['class' => 'fit-cover', 'alt' => '']);
|
||
if ($thumb === '') {
|
||
$thumb = '<div class="pay-thumb-placeholder"><i class="fa fa-lock" aria-hidden="true"></i></div>';
|
||
}
|
||
$graphic_actions = '';
|
||
if ($demo !== '' || $discount_text !== '') {
|
||
$graphic_actions = '<div class="abs-center text-center left-bottom">';
|
||
$graphic_actions .= $demo !== '' ? '<div>' . $demo . '</div>' : '';
|
||
$graphic_actions .= $discount_text !== '' ? '<div class="padding-6 jb-red px12 hidden-xs">' . esc_html($discount_text) . '</div>' : '';
|
||
$graphic_actions .= '</div>';
|
||
}
|
||
$discount_mobile = $discount_text !== '' ? '<div class="visible-xs-block badg c-red px12 mb6">' . esc_html($discount_text) . '</div>' : '';
|
||
if ($pay_type === AKSRT_PAY_TYPE_GALLERY) {
|
||
$gallery_count = count((array) ($meta['gallery'] ?? []));
|
||
$graphic = '<div class="flex0 relative mr20 hide-sm pay-thumb"><div class="graphic"><div class="blur-10 absolute">' . $thumb . '</div><div class="absolute graphic-mask" style="opacity:.3"></div><div class="abs-center text-center">' . esc_html(sprintf(__('共%s张图片', 'aksrt'), $gallery_count)) . '<br>' . esc_html__('请付费后查看', 'aksrt') . '</div>' . $graphic_actions . '</div></div>';
|
||
} elseif ($pay_type === AKSRT_PAY_TYPE_VIDEO) {
|
||
$graphic = '<div class="flex0 relative mr20 hide-sm pay-thumb"><div class="graphic">' . $thumb . '<div class="absolute graphic-mask" style="opacity:.2"></div><div class="abs-center text-center"><i class="fa fa-play-circle-o fa-4x opacity8" aria-hidden="true"></i></div>' . $graphic_actions . '</div></div>';
|
||
} else {
|
||
$graphic = '<div class="flex0 relative mr20 hide-sm pay-thumb"><div class="graphic">' . $thumb . $graphic_actions . '</div></div>';
|
||
}
|
||
$demo_mobile = $demo !== '' ? '<div class="mt10 visible-xs-block">' . $demo . '</div>' : '';
|
||
return '<div class="zib-widget pay-box order-type-' . $order_type . '" id="posts-pay"><div class="flex pay-flexbox">' . $graphic . '<div class="flex-auto-h flex xx jsb"><dt class="text-ellipsis pay-title">' . esc_html($title) . '</dt><div class="mt6 em09 muted-2-color">' . wp_kses_post($doc) . '</div><div>' . $price . '</div>' . $discount_mobile . ($vip !== '' ? '<div>' . $vip . '</div>' : '') . '<div class="text-right mt10">' . $button . '</div></div></div><div class="px12 muted-2-color mt10 text-right">' . $service . '</div>' . $demo_mobile . $attrs . $tag . $count . '</div>';
|
||
}
|
||
}
|
||
|
||
// ============================================================
|
||
// 付费图库轮播(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 '';
|
||
|
||
if (!aksrt_has_paid_access($post)) return '';
|
||
|
||
ob_start();
|
||
?>
|
||
<div class="aksrt-paid-gallery" data-post-id="<?php echo esc_attr((string) $post->ID); ?>">
|
||
<div class="new-swiper aksrt-paid-gallery__swiper" data-loop="true">
|
||
<div class="swiper-wrapper">
|
||
<?php foreach ($images as $img_url) : ?>
|
||
<div class="swiper-slide">
|
||
<img<?php echo aksrt_get_lazy_attr('lazy_other', (string) $img_url, '', aksrt_get_lazy_thumb('lg')); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> alt="">
|
||
</div>
|
||
<?php endforeach; ?>
|
||
</div>
|
||
<div class="swiper-pagination"></div>
|
||
<div class="swiper-button-prev"></div>
|
||
<div class="swiper-button-next"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<?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 '';
|
||
|
||
if (!aksrt_has_paid_access($post)) return '';
|
||
|
||
ob_start();
|
||
?>
|
||
<div class="aksrt-paid-video" data-post-id="<?php echo esc_attr((string) $post->ID); ?>">
|
||
<div class="aksrt-paid-video__player">
|
||
<?php if (preg_match('/\.(mp4|webm|ogg)$/i', $video_url)) : ?>
|
||
<video controls preload="metadata"<?php echo aksrt_is_lazy('lazy_posts_content') ? ' class="lazyload" src="" data-src="' . esc_url($video_url) . '"' : ' src="' . esc_url($video_url) . '"'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> style="width:100%;border-radius:var(--radius-lg);">
|
||
<?php esc_html_e('您的浏览器不支持视频播放。', 'aksrt'); ?>
|
||
</video>
|
||
<?php else : ?>
|
||
<div class="aksrt-paid-video__embed">
|
||
<iframe<?php echo aksrt_is_lazy('lazy_posts_content') ? ' class="lazyload" src="" data-src="' . esc_url($video_url) . '"' : ' src="' . esc_url($video_url) . '"'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> frameborder="0" allowfullscreen></iframe>
|
||
</div>
|
||
<?php endif; ?>
|
||
</div>
|
||
</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() || !in_the_loop() || !is_main_query()) {
|
||
return $content;
|
||
}
|
||
global $post;
|
||
if (!($post instanceof WP_Post)) {
|
||
return $content;
|
||
}
|
||
$meta = aksrt_get_pay_meta($post);
|
||
$pay_type = (string) ($meta['pay_type'] ?? AKSRT_PAY_TYPE_NO);
|
||
// Reading is protected only by the payshow shortcode. Never replace
|
||
// the entire article body with a post-level purchase wall.
|
||
if ($pay_type === AKSRT_PAY_TYPE_READING) {
|
||
return $content;
|
||
}
|
||
if (aksrt_can_access_content($post)) {
|
||
return $content;
|
||
}
|
||
if (!in_array($pay_type, [AKSRT_PAY_TYPE_GALLERY, AKSRT_PAY_TYPE_VIDEO], true)) {
|
||
return $content;
|
||
}
|
||
|
||
// Only the configured teaser remains in the article body. The single
|
||
// template owns the one canonical purchase card for gallery/video.
|
||
$preview = trim((string) ($meta['free_preview'] ?? ''));
|
||
return $preview !== '' ? '<div class="aksrt-paywall-preview">' . wp_kses_post($preview) . '</div>' : '';
|
||
}
|
||
}
|
||
|
||
// ============================================================
|
||
// 付费图库/视频样式
|
||
// ============================================================
|
||
|
||
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-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}
|
||
</style>
|
||
<?php
|
||
});
|