Files
wordpress-theme-aksrt/inc/classes/class-aksrt-resource-meta-box.php
T
kate522 1c8e4d3919 feat(pay): zibll-style pay_modo/points/pay_limit, VIP quota, and local speed stream
Expand post pay meta with cash/points modes and member purchase limits,
enforce free-download daily quotas, and stream local files with VIP speed
caps while keeping remote links on redirect.
2026-07-14 01:11:33 +08:00

532 lines
30 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/**
* AKSRT Pay Meta Box — 统一付费内容设置
*
* 模仿 zibll 的 posts_zibpay 机制:
* - 任何 post 都可以通过设置付费类型成为付费文章
* - pay_type 驱动行为(reading/download/gallery/video/vip/no)
* - 统一存储为 _aksrt_pay 序列化数组
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
class AKSRT_Resource_Meta_Box {
private static $instance = null;
private function __construct() {}
public static function get_instance(): self {
if (null === self::$instance) self::$instance = new self();
return self::$instance;
}
public function init(): void {
add_action('add_meta_boxes', [$this, 'register_meta_box']);
add_action('save_post_post', [$this, 'save'], 10, 2);
}
public function register_meta_box(): void {
add_meta_box(
'aksrt_pay_meta',
__('付费内容设置', 'aksrt'),
[$this, 'render'],
'post',
'normal',
'high'
);
}
public function render(WP_Post $post): void {
wp_nonce_field('aksrt_pay_meta_save', 'aksrt_pay_meta_nonce');
$meta = function_exists('aksrt_get_pay_meta')
? aksrt_get_pay_meta($post)
: AKSRT_Domain_Model::default_pay_meta();
$pay_types = aksrt_get_pay_types();
$is_paid = ($meta['pay_type'] ?? 'no') !== 'no';
$downloads_json = !empty($meta['downloads'])
? wp_json_encode($meta['downloads'], JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT)
: '';
$attributes_json = !empty($meta['attributes'])
? wp_json_encode($meta['attributes'], JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT)
: '';
$gallery_json = !empty($meta['gallery'])
? wp_json_encode($meta['gallery'], JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT)
: '';
?>
<style>
.aksrt-pmb__row { display: grid; grid-template-columns: 140px 1fr; gap: 12px 16px; align-items: start; margin-bottom: 12px; }
.aksrt-pmb__row label { font-weight: 600; color: #1f2937; padding-top: 6px; font-size: 13px; }
.aksrt-pmb__row input[type="text"], .aksrt-pmb__row input[type="number"], .aksrt-pmb__row input[type="url"], .aksrt-pmb__row textarea, .aksrt-pmb__row select { width: 100%; max-width: 560px; }
.aksrt-pmb__row textarea { min-height: 120px; font-family: Consolas, "Liberation Mono", monospace; font-size: 12px; }
.aksrt-pmb__hint { color: #6b7280; font-size: 12px; margin-top: 4px; line-height: 1.5; }
.aksrt-pmb__section { margin-top: 16px; padding-top: 16px; border-top: 1px solid #e5e7eb; }
.aksrt-pmb__section-title { font-size: 14px; font-weight: 700; color: #111827; margin-bottom: 12px; }
.aksrt-pmb__toggle { display: flex; align-items: center; gap: 8px; padding: 12px; background: #f3f4f6; border-radius: 6px; margin-bottom: 16px; }
.aksrt-pmb__toggle label { font-weight: 600; }
.aksrt-pmb__vip-row { display: flex; gap: 12px; flex-wrap: wrap; }
.aksrt-pmb__vip-row > div { flex: 1; min-width: 120px; }
.aksrt-pmb__vip-row label { font-weight: 400; font-size: 12px; color: #6b7280; }
.aksrt-pmb__tools { margin-top: 6px; display: flex; gap: 6px; flex-wrap: wrap; }
.aksrt-pmb__tools button { font-size: 12px; }
.aksrt-pmb__error { color: #dc2626; font-size: 12px; display: none; margin-top: 4px; }
.aksrt-pmb__fields[data-disabled="1"] { opacity: 0.45; pointer-events: none; }
</style>
<div class="aksrt-pmb__toggle">
<label for="aksrt_pay_type"><?php esc_html_e('付费类型', 'aksrt'); ?></label>
<select id="aksrt_pay_type" name="aksrt_pay_type" style="min-width:160px;">
<?php foreach ($pay_types as $key => $label) : ?>
<option value="<?php echo esc_attr($key); ?>" <?php selected($meta['pay_type'], $key); ?>>
<?php echo esc_html($label); ?>
</option>
<?php endforeach; ?>
</select>
<span class="aksrt-pmb__hint">
<?php esc_html_e('选择「不付费」则为普通文章;选择其他类型将启用对应的付费功能。', 'aksrt'); ?>
</span>
</div>
<div class="aksrt-pmb__fields" id="aksrt-pmb-fields" data-disabled="<?php echo $is_paid ? '0' : '1'; ?>">
<!-- 基础价格 -->
<div class="aksrt-pmb__section">
<div class="aksrt-pmb__section-title"><?php esc_html_e('价格设置', 'aksrt'); ?></div>
<div class="aksrt-pmb__row">
<label for="aksrt_pay_modo"><?php esc_html_e('支付方式', 'aksrt'); ?></label>
<div>
<select id="aksrt_pay_modo" name="aksrt_pay_modo" style="min-width:160px;">
<option value="0" <?php selected((string) ($meta['pay_modo'] ?? '0'), '0'); ?>><?php esc_html_e('现金', 'aksrt'); ?></option>
<option value="points" <?php selected((string) ($meta['pay_modo'] ?? '0'), 'points'); ?>><?php esc_html_e('积分', 'aksrt'); ?></option>
</select>
<p class="aksrt-pmb__hint"><?php esc_html_e('对齐 zibll pay_modo:现金价或积分价结算。', 'aksrt'); ?></p>
</div>
</div>
<div class="aksrt-pmb__row">
<label for="aksrt_pay_price"><?php esc_html_e('现金价格', 'aksrt'); ?></label>
<div>
<input type="number" id="aksrt_pay_price" name="aksrt_pay_price" value="<?php echo esc_attr((string) $meta['price']); ?>" min="0" step="0.01">
<p class="aksrt-pmb__hint"><?php esc_html_e('支付方式为「现金」时使用。0 = 免费。', 'aksrt'); ?></p>
</div>
</div>
<div class="aksrt-pmb__row">
<label for="aksrt_pay_points_price"><?php esc_html_e('积分价格', 'aksrt'); ?></label>
<div>
<input type="number" id="aksrt_pay_points_price" name="aksrt_pay_points_price" value="<?php echo esc_attr((string) ($meta['points_price'] ?? 0)); ?>" min="0" step="1">
<p class="aksrt-pmb__hint"><?php esc_html_e('支付方式为「积分」时使用。0 = 免费。', 'aksrt'); ?></p>
</div>
</div>
<div class="aksrt-pmb__row">
<label for="aksrt_pay_original_price"><?php esc_html_e('原价(划线价)', 'aksrt'); ?></label>
<div>
<input type="number" id="aksrt_pay_original_price" name="aksrt_pay_original_price" value="<?php echo esc_attr((string) ($meta['original_price'] ?? '')); ?>" min="0" step="0.01" placeholder="<?php esc_attr_e('可选,显示划线效果', 'aksrt'); ?>">
</div>
</div>
<div class="aksrt-pmb__row">
<label for="aksrt_pay_limit"><?php esc_html_e('购买限制', 'aksrt'); ?></label>
<div>
<select id="aksrt_pay_limit" name="aksrt_pay_limit" style="min-width:200px;">
<option value="0" <?php selected((int) ($meta['pay_limit'] ?? 0), 0); ?>><?php esc_html_e('所有用户可购买', 'aksrt'); ?></option>
<option value="1" <?php selected((int) ($meta['pay_limit'] ?? 0), 1); ?>><?php esc_html_e('仅 VIP1 及以上', 'aksrt'); ?></option>
<option value="2" <?php selected((int) ($meta['pay_limit'] ?? 0), 2); ?>><?php esc_html_e('仅 VIP2', 'aksrt'); ?></option>
</select>
<p class="aksrt-pmb__hint"><?php esc_html_e('对齐 zibll pay_limit:限制购买人群(会员专属资源)。', 'aksrt'); ?></p>
</div>
</div>
<div class="aksrt-pmb__row">
<label><?php esc_html_e('VIP 专属价', 'aksrt'); ?></label>
<div>
<div class="aksrt-pmb__vip-row">
<div>
<label for="aksrt_pay_vip1_price"><?php esc_html_e('VIP1 现金价', 'aksrt'); ?></label>
<input type="number" id="aksrt_pay_vip1_price" name="aksrt_pay_vip1_price" value="<?php echo esc_attr((string) ($meta['vip1_price'] ?? '')); ?>" min="0" step="0.01" placeholder="0">
</div>
<div>
<label for="aksrt_pay_vip2_price"><?php esc_html_e('VIP2 现金价', 'aksrt'); ?></label>
<input type="number" id="aksrt_pay_vip2_price" name="aksrt_pay_vip2_price" value="<?php echo esc_attr((string) ($meta['vip2_price'] ?? '')); ?>" min="0" step="0.01" placeholder="0">
</div>
<div>
<label for="aksrt_pay_vip1_points"><?php esc_html_e('VIP1 积分价', 'aksrt'); ?></label>
<input type="number" id="aksrt_pay_vip1_points" name="aksrt_pay_vip1_points" value="<?php echo esc_attr((string) ($meta['vip1_points'] ?? '')); ?>" min="0" step="1" placeholder="0">
</div>
<div>
<label for="aksrt_pay_vip2_points"><?php esc_html_e('VIP2 积分价', 'aksrt'); ?></label>
<input type="number" id="aksrt_pay_vip2_points" name="aksrt_pay_vip2_points" value="<?php echo esc_attr((string) ($meta['vip2_points'] ?? '')); ?>" min="0" step="1" placeholder="0">
</div>
</div>
<p class="aksrt-pmb__hint"><?php esc_html_e('VIP 用户专属价。留空或 0 表示不启用该档位优惠。', 'aksrt'); ?></p>
</div>
</div>
</div>
<!-- 资源信息(download/gallery/video 类型显示) -->
<div class="aksrt-pmb__section" id="aksrt-pmb-resource-fields">
<div class="aksrt-pmb__section-title"><?php esc_html_e('资源信息', 'aksrt'); ?></div>
<div class="aksrt-pmb__row">
<label for="aksrt_pay_version"><?php esc_html_e('版本', 'aksrt'); ?></label>
<input type="text" id="aksrt_pay_version" name="aksrt_pay_version" value="<?php echo esc_attr((string) $meta['version']); ?>" placeholder="v1.0.0">
</div>
<div class="aksrt-pmb__row">
<label for="aksrt_pay_size"><?php esc_html_e('文件大小', 'aksrt'); ?></label>
<input type="text" id="aksrt_pay_size" name="aksrt_pay_size" value="<?php echo esc_attr((string) $meta['size']); ?>" placeholder="210 MB">
</div>
<div class="aksrt-pmb__row">
<label for="aksrt_pay_compat"><?php esc_html_e('兼容性', 'aksrt'); ?></label>
<input type="text" id="aksrt_pay_compat" name="aksrt_pay_compat" value="<?php echo esc_attr((string) $meta['compat']); ?>" placeholder="WordPress 6.4+">
</div>
<div class="aksrt-pmb__row">
<label for="aksrt_pay_license"><?php esc_html_e('授权', 'aksrt'); ?></label>
<input type="text" id="aksrt_pay_license" name="aksrt_pay_license" value="<?php echo esc_attr((string) $meta['license']); ?>" placeholder="MIT / GPL / 自用">
</div>
<div class="aksrt-pmb__row">
<label for="aksrt_pay_demo_url"><?php esc_html_e('演示地址', 'aksrt'); ?></label>
<input type="url" id="aksrt_pay_demo_url" name="aksrt_pay_demo_url" value="<?php echo esc_attr((string) $meta['demo_url']); ?>" placeholder="https://">
</div>
</div>
<!-- 自定义属性 -->
<div class="aksrt-pmb__section">
<div class="aksrt-pmb__section-title"><?php esc_html_e('自定义属性', 'aksrt'); ?></div>
<div class="aksrt-pmb__row">
<label for="aksrt_pay_attributes"><?php esc_html_e('属性列表', 'aksrt'); ?></label>
<div>
<textarea id="aksrt_pay_attributes" name="aksrt_pay_attributes" placeholder='[
{"key": "格式", "value": "PSD"},
{"key": "分辨率", "value": "4K"}
]'><?php echo esc_textarea($attributes_json); ?></textarea>
<p class="aksrt-pmb__hint"><?php esc_html_e('JSON 数组。每项:key(属性名)/ value(属性值)。', 'aksrt'); ?></p>
</div>
</div>
</div>
<!-- 下载源(download 类型显示) -->
<div class="aksrt-pmb__section" id="aksrt-pmb-download-fields">
<div class="aksrt-pmb__section-title"><?php esc_html_e('下载源', 'aksrt'); ?></div>
<div class="aksrt-pmb__row">
<label for="aksrt_pay_downloads"><?php esc_html_e('下载链接', 'aksrt'); ?></label>
<div>
<textarea id="aksrt_pay_downloads" name="aksrt_pay_downloads" placeholder='[
{
"label": "百度网盘",
"url": "https://pan.baidu.com/s/xxx",
"type": "net_disk",
"extract_code": "abcd"
}
]'><?php echo esc_textarea($downloads_json); ?></textarea>
<div class="aksrt-pmb__error" id="aksrt-pmb-dl-err"></div>
<div class="aksrt-pmb__tools">
<button type="button" class="button button-small" id="aksrt-pmb-add-dl"><?php esc_html_e('追加示例条目', 'aksrt'); ?></button>
<button type="button" class="button button-small" id="aksrt-pmb-fmt-dl"><?php esc_html_e('格式化 JSON', 'aksrt'); ?></button>
</div>
<p class="aksrt-pmb__hint"><?php esc_html_e('JSON 数组。每项:label / url / type(direct | net_disk | external)/ extract_code。', 'aksrt'); ?></p>
</div>
</div>
</div>
<!-- 图库 URL(gallery 类型显示) -->
<div class="aksrt-pmb__section" id="aksrt-pmb-gallery-fields" style="display:none;">
<div class="aksrt-pmb__section-title"><?php esc_html_e('付费图库', 'aksrt'); ?></div>
<div class="aksrt-pmb__row">
<label for="aksrt_pay_gallery"><?php esc_html_e('图片 URL 列表', 'aksrt'); ?></label>
<div>
<textarea id="aksrt_pay_gallery" name="aksrt_pay_gallery" placeholder='[
"https://example.com/img1.jpg",
"https://example.com/img2.jpg"
]'><?php echo esc_textarea($gallery_json); ?></textarea>
<p class="aksrt-pmb__hint"><?php esc_html_e('JSON 数组。每项为图片 URL。付费前图片将被模糊遮罩。', 'aksrt'); ?></p>
</div>
</div>
</div>
<!-- 视频 URL(video 类型显示) -->
<div class="aksrt-pmb__section" id="aksrt-pmb-video-fields" style="display:none;">
<div class="aksrt-pmb__section-title"><?php esc_html_e('付费视频', 'aksrt'); ?></div>
<div class="aksrt-pmb__row">
<label for="aksrt_pay_video_url"><?php esc_html_e('视频 URL', 'aksrt'); ?></label>
<div>
<input type="url" id="aksrt_pay_video_url" name="aksrt_pay_video_url" value="<?php echo esc_attr((string) ($meta['video_url'] ?? '')); ?>" placeholder="https://example.com/video.mp4">
<p class="aksrt-pmb__hint"><?php esc_html_e('支持 MP4 直链或视频平台嵌入链接。', 'aksrt'); ?></p>
</div>
</div>
</div>
<!-- 选项覆盖 -->
<div class="aksrt-pmb__section">
<div class="aksrt-pmb__section-title"><?php esc_html_e('选项覆盖', 'aksrt'); ?></div>
<div class="aksrt-pmb__row">
<label><?php esc_html_e('单条覆盖', 'aksrt'); ?></label>
<div>
<div style="display:flex; align-items:center; gap:6px; margin-bottom:6px;">
<input type="checkbox" id="aksrt_pay_require_login" name="aksrt_pay_require_login" value="1" <?php checked($meta['require_login']); ?>>
<label for="aksrt_pay_require_login" style="font-weight:400;padding:0;"><?php esc_html_e('强制登录后才可访问', 'aksrt'); ?></label>
</div>
<div style="display:flex; align-items:center; gap:6px;">
<input type="checkbox" id="aksrt_pay_require_captcha" name="aksrt_pay_require_captcha" value="1" <?php checked($meta['require_captcha']); ?>>
<label for="aksrt_pay_require_captcha" style="font-weight:400;padding:0;"><?php esc_html_e('强制需要人机验证', 'aksrt'); ?></label>
</div>
<p class="aksrt-pmb__hint"><?php esc_html_e('全局开关在后台统一控制,这里仅作为单条强制覆盖。', 'aksrt'); ?></p>
</div>
</div>
</div>
<!-- §3.8 电商增强 -->
<div class="aksrt-pmb__section">
<div class="aksrt-pmb__section-title"><?php esc_html_e('电商增强', 'aksrt'); ?></div>
<div class="aksrt-pmb__row">
<label for="aksrt_pay_virtual_sales"><?php esc_html_e('虚拟销量', 'aksrt'); ?></label>
<div>
<input type="number" id="aksrt_pay_virtual_sales" name="aksrt_pay_virtual_sales" value="<?php echo esc_attr((string) ($meta['virtual_sales'] ?? 0)); ?>" min="0" step="1">
<p class="aksrt-pmb__hint"><?php esc_html_e('在前端展示的销量基础上叠加此数值,用于展示效果。', 'aksrt'); ?></p>
</div>
</div>
<div class="aksrt-pmb__row">
<label for="aksrt_pay_stock"><?php esc_html_e('库存', 'aksrt'); ?></label>
<div>
<input type="number" id="aksrt_pay_stock" name="aksrt_pay_stock" value="<?php echo esc_attr((string) ($meta['stock'] ?? -1)); ?>" min="-1" step="1">
<p class="aksrt-pmb__hint"><?php esc_html_e('-1 表示无限库存;>0 表示限量,售完即止。', 'aksrt'); ?></p>
</div>
</div>
<div class="aksrt-pmb__row">
<label for="aksrt_pay_purchase_limit"><?php esc_html_e('限购数量', 'aksrt'); ?></label>
<div>
<input type="number" id="aksrt_pay_purchase_limit" name="aksrt_pay_purchase_limit" value="<?php echo esc_attr((string) ($meta['purchase_limit'] ?? 0)); ?>" min="0" step="1">
<p class="aksrt-pmb__hint"><?php esc_html_e('每用户最多可购买次数,0 表示不限购。', 'aksrt'); ?></p>
</div>
</div>
<div class="aksrt-pmb__row">
<label for="aksrt_pay_card_keys"><?php esc_html_e('自动发货卡密', 'aksrt'); ?></label>
<div>
<textarea id="aksrt_pay_card_keys" name="aksrt_pay_card_keys" placeholder="<?php esc_attr_e('每行一个卡密/激活码,购买后自动发放一个', 'aksrt'); ?>"><?php echo esc_textarea(implode("\n", $meta['card_keys'] ?? [])); ?></textarea>
<p class="aksrt-pmb__hint"><?php esc_html_e('每行一个卡密,购买后自动消耗一个并发给用户。留空则不自动发货。', 'aksrt'); ?></p>
</div>
</div>
<div class="aksrt-pmb__row">
<label for="aksrt_pay_free_preview"><?php esc_html_e('免费试读段落', 'aksrt'); ?></label>
<div>
<textarea id="aksrt_pay_free_preview" name="aksrt_pay_free_preview" style="min-height:80px;" placeholder="<?php esc_attr_e('付费内容的免费预览段落(支持 HTML)', 'aksrt'); ?>"><?php echo esc_textarea($meta['free_preview'] ?? ''); ?></textarea>
<p class="aksrt-pmb__hint"><?php esc_html_e('未付费用户可见的预览内容,支持基础 HTML。留空则不显示预览。', 'aksrt'); ?></p>
</div>
</div>
<div class="aksrt-pmb__row">
<label><?php esc_html_e('销量显示', 'aksrt'); ?></label>
<div style="display:flex; align-items:center; gap:6px;">
<input type="checkbox" id="aksrt_pay_show_sales" name="aksrt_pay_show_sales" value="1" <?php checked($meta['show_sales'] ?? true); ?>>
<label for="aksrt_pay_show_sales" style="font-weight:400;padding:0;"><?php esc_html_e('在前端显示销量', 'aksrt'); ?></label>
</div>
</div>
</div>
</div>
<script>
(function () {
var typeSelect = document.getElementById('aksrt_pay_type');
var fields = document.getElementById('aksrt-pmb-fields');
var dlFields = document.getElementById('aksrt-pmb-download-fields');
var galleryFields = document.getElementById('aksrt-pmb-gallery-fields');
var videoFields = document.getElementById('aksrt-pmb-video-fields');
var resourceFields = document.getElementById('aksrt-pmb-resource-fields');
function updateVisibility() {
var t = typeSelect.value;
var isPaid = t !== 'no';
fields.setAttribute('data-disabled', isPaid ? '0' : '1');
// 根据付费类型显示/隐藏相关字段
if (dlFields) dlFields.style.display = (t === 'download') ? '' : 'none';
if (galleryFields) galleryFields.style.display = (t === 'gallery') ? '' : 'none';
if (videoFields) videoFields.style.display = (t === 'video') ? '' : 'none';
if (resourceFields) resourceFields.style.display = (t === 'download' || t === 'gallery' || t === 'video') ? '' : 'none';
}
if (typeSelect) {
typeSelect.addEventListener('change', updateVisibility);
updateVisibility();
}
// 下载源 JSON 工具
var dlTa = document.getElementById('aksrt_pay_downloads');
var dlErr = document.getElementById('aksrt-pmb-dl-err');
var addDlBtn = document.getElementById('aksrt-pmb-add-dl');
var fmtDlBtn = document.getElementById('aksrt-pmb-fmt-dl');
if (addDlBtn && dlTa) {
addDlBtn.addEventListener('click', function () {
var sample = {label: '百度网盘', url: 'https://pan.baidu.com/s/xxx', type: 'net_disk', extract_code: ''};
try {
var arr = dlTa.value.trim() ? JSON.parse(dlTa.value) : [];
arr.push(sample);
dlTa.value = JSON.stringify(arr, null, 2);
if (dlErr) dlErr.style.display = 'none';
} catch (e) {
if (dlErr) { dlErr.textContent = 'JSON 解析错误: ' + e.message; dlErr.style.display = 'block'; }
}
});
}
if (fmtDlBtn && dlTa) {
fmtDlBtn.addEventListener('click', function () {
try {
var arr = JSON.parse(dlTa.value);
dlTa.value = JSON.stringify(arr, null, 2);
if (dlErr) dlErr.style.display = 'none';
} catch (e) {
if (dlErr) { dlErr.textContent = 'JSON 解析错误: ' + e.message; dlErr.style.display = 'block'; }
}
});
}
})();
</script>
<?php
}
public function save(int $post_id, WP_Post $post): void {
if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) return;
if (wp_is_post_revision($post_id)) return;
if ($post->post_type !== 'post') return;
if (empty($_POST['aksrt_pay_meta_nonce'])
|| !wp_verify_nonce((string) $_POST['aksrt_pay_meta_nonce'], 'aksrt_pay_meta_save')) {
return;
}
if (!current_user_can('edit_post', $post_id)) return;
// 付费类型
$allowed_types = array_keys(aksrt_get_pay_types());
$pay_type = sanitize_key((string) ($_POST['aksrt_pay_type'] ?? 'no'));
if (!in_array($pay_type, $allowed_types, true)) {
$pay_type = 'no';
}
// 价格字段
$pay_modo = sanitize_key((string) ($_POST['aksrt_pay_modo'] ?? '0'));
if ($pay_modo !== 'points') {
$pay_modo = '0';
}
$price = max(0, (float) ($_POST['aksrt_pay_price'] ?? 0));
$points_price = max(0, (int) ($_POST['aksrt_pay_points_price'] ?? 0));
// Keep legacy single-price editors usable: if points field empty and modo points, mirror price.
if ($pay_modo === 'points' && $points_price <= 0 && $price > 0) {
$points_price = (int) ceil($price);
}
if ($pay_modo !== 'points' && $price <= 0 && $points_price > 0) {
$price = (float) $points_price;
}
$original_price = max(0, (float) ($_POST['aksrt_pay_original_price'] ?? 0));
$vip1_price = max(0, (float) ($_POST['aksrt_pay_vip1_price'] ?? 0));
$vip2_price = max(0, (float) ($_POST['aksrt_pay_vip2_price'] ?? 0));
$vip1_points = max(0, (int) ($_POST['aksrt_pay_vip1_points'] ?? 0));
$vip2_points = max(0, (int) ($_POST['aksrt_pay_vip2_points'] ?? 0));
$pay_limit = (int) ($_POST['aksrt_pay_limit'] ?? 0);
if (!in_array($pay_limit, [0, 1, 2], true)) {
$pay_limit = 0;
}
// 资源信息
$version = sanitize_text_field((string) ($_POST['aksrt_pay_version'] ?? ''));
$size = sanitize_text_field((string) ($_POST['aksrt_pay_size'] ?? ''));
$compat = sanitize_text_field((string) ($_POST['aksrt_pay_compat'] ?? ''));
$license = sanitize_text_field((string) ($_POST['aksrt_pay_license'] ?? ''));
$demo_url = esc_url_raw((string) ($_POST['aksrt_pay_demo_url'] ?? ''));
$video_url = esc_url_raw((string) ($_POST['aksrt_pay_video_url'] ?? ''));
// 布尔字段
$require_login = !empty($_POST['aksrt_pay_require_login']);
$require_captcha = !empty($_POST['aksrt_pay_require_captcha']);
// JSON 字段
$downloads = $this->parse_json_array($_POST['aksrt_pay_downloads'] ?? '');
$attributes = $this->parse_json_array($_POST['aksrt_pay_attributes'] ?? '');
$gallery = $this->parse_json_array($_POST['aksrt_pay_gallery'] ?? '');
// 清洗下载源
if ($downloads !== null && class_exists('AKSRT_Setup') && method_exists('AKSRT_Setup', 'sanitize_downloads_meta')) {
$downloads = AKSRT_Setup::sanitize_downloads_meta($downloads);
}
// 保存统一元数据
$pay_meta = AKSRT_Domain_Model::sanitize_pay_meta([
'pay_type' => $pay_type,
'pay_modo' => $pay_modo,
'price' => $price,
'points_price' => $points_price,
'original_price' => $original_price,
'vip1_price' => $vip1_price,
'vip2_price' => $vip2_price,
'vip1_points' => $vip1_points,
'vip2_points' => $vip2_points,
'pay_limit' => $pay_limit,
'version' => $version,
'size' => $size,
'compat' => $compat,
'license' => $license,
'demo_url' => $demo_url,
'video_url' => $video_url,
'downloads' => $downloads ?? [],
'attributes' => $attributes ?? [],
'gallery' => $gallery ?? [],
'require_login' => $require_login,
'require_captcha' => $require_captcha,
// §3.8 电商增强字段
'virtual_sales' => $_POST['aksrt_pay_virtual_sales'] ?? 0,
'stock' => $_POST['aksrt_pay_stock'] ?? -1,
'purchase_limit' => $_POST['aksrt_pay_purchase_limit'] ?? 0,
'card_keys' => isset($_POST['aksrt_pay_card_keys']) ? array_filter(array_map('trim', explode("\n", (string) $_POST['aksrt_pay_card_keys']))) : [],
'free_preview' => $_POST['aksrt_pay_free_preview'] ?? '',
'show_sales' => !empty($_POST['aksrt_pay_show_sales']),
]);
update_post_meta($post_id, AKSRT_Domain_Model::PAY_META_KEY, $pay_meta);
// 同步标量字段用于查询(模仿 zibll 的 zibpay_type 同步)
update_post_meta($post_id, AKSRT_Domain_Model::PAY_TYPE_KEY, (string) $pay_meta['pay_type']);
$query_price = ($pay_meta['pay_modo'] === 'points')
? (float) $pay_meta['points_price']
: (float) $pay_meta['price'];
update_post_meta($post_id, AKSRT_Domain_Model::PAY_PRICE_KEY, $query_price);
update_post_meta($post_id, '_aksrt_pay_modo', (string) $pay_meta['pay_modo']);
update_post_meta($post_id, '_aksrt_pay_limit', (int) $pay_meta['pay_limit']);
// 清理旧的 _aksrt_is_resource 标记
update_post_meta(
$post_id,
AKSRT_Domain_Model::RESOURCE_MARKER_KEY,
AKSRT_Domain_Model::is_resource_pay_type((string) $pay_meta['pay_type']) ? 1 : 0
);
}
/**
* 安全解析 JSON 数组。
*/
private function parse_json_array(string $raw): ?array {
$raw = trim($raw);
if ($raw === '') return [];
$decoded = json_decode($raw, true);
return is_array($decoded) ? $decoded : null;
}
}