From 562beb15cfd97cdafe71d95af6f5f1ad0deeed74 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 26 Aug 2026 15:29:37 +0800 Subject: [PATCH] =?UTF-8?q?=E8=BF=9B=E8=A1=8C=E4=BA=86=E4=B8=80=E4=BA=9B?= =?UTF-8?q?=E5=B0=8F=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/js/cms-download.js | 22 +- inc/helpers/aksrt-pay.php | 194 +++++++++++++++++- inc/helpers/aksrt-single-render.php | 17 +- inc/helpers/aksrt-single.php | 6 +- templates/parts/resource-download-box.php | 1 + .../widget-system/pay-card-contract.test.cjs | 9 + 6 files changed, 206 insertions(+), 43 deletions(-) diff --git a/assets/js/cms-download.js b/assets/js/cms-download.js index 81a0bff3..38da0f35 100644 --- a/assets/js/cms-download.js +++ b/assets/js/cms-download.js @@ -228,14 +228,8 @@ // ===== 主流程 ===== function collectCaptchaPayload(sourceEl) { - // 优先从触发按钮所属的下载卡片中查找验证码容器(支持双下载框场景:侧栏+移动端) - var container = null; - if (sourceEl) { - var box = sourceEl.closest('.resource-download-box'); - if (box) container = box.querySelector('[data-aksrt-download-captcha]'); - } - // 回退到全局查找 - if (!container) container = document.querySelector('[data-aksrt-download-captcha]'); + const box = sourceEl ? sourceEl.closest('[data-aksrt-download-box]') : null; + const container = box ? box.querySelector('[data-aksrt-download-captcha]') : null; if (!container) return {}; const out = {}; const keys = [ @@ -252,14 +246,14 @@ return out; } - function isCaptchaActive() { - const card = document.querySelector('.resource-download-box'); + function isCaptchaActive(sourceEl) { + const card = sourceEl ? sourceEl.closest('[data-aksrt-download-box]') : null; if (!card) return false; return card.getAttribute('data-captcha') === '1'; } - function ensureCaptchaThen(callback) { - if (!isCaptchaActive()) { callback(); return; } + function ensureCaptchaThen(sourceEl, callback) { + if (!isCaptchaActive(sourceEl)) { callback(); return; } const state = window.AKSRTCaptchaState; const scene = state && state.scenes && state.scenes.download; if (!scene || typeof scene.verify !== 'function') { @@ -274,7 +268,7 @@ } function performDownloadRequest(button) { - const card = button.closest('.resource-download-box'); + const card = button.closest('[data-aksrt-download-box]'); if (!card) return; const postId = parseInt(card.getAttribute('data-post-id'), 10); const sourceIndex = parseInt(button.getAttribute('data-source-index'), 10); @@ -323,7 +317,7 @@ } function requestDownload(button) { - ensureCaptchaThen(function () { + ensureCaptchaThen(button, function () { performDownloadRequest(button); }); } diff --git a/inc/helpers/aksrt-pay.php b/inc/helpers/aksrt-pay.php index d7d2552e..6db12403 100644 --- a/inc/helpers/aksrt-pay.php +++ b/inc/helpers/aksrt-pay.php @@ -1300,6 +1300,176 @@ if (!function_exists('aksrt_get_paywall_html')) { } } +/** + * Resolve the paid order that granted access to a post, when access came from + * an actual purchase rather than a free or VIP entitlement. + * + * @return array|null + */ +if (!function_exists('aksrt_get_current_content_paid_order')) { + function aksrt_get_current_content_paid_order(WP_Post $post): ?array { + if (is_user_logged_in()) { + return aksrt_get_user_content_paid_order($post->ID); + } + + return aksrt_get_guest_content_paid_order($post->ID); + } +} + +/** + * Reference-theme download actions used inside the purchased article card. + */ +if (!function_exists('aksrt_get_paid_download_actions_html')) { + function aksrt_get_paid_download_actions_html(array $meta): string { + $downloads = is_array($meta['downloads'] ?? null) ? array_values($meta['downloads']) : []; + if ($downloads === []) { + return '
' . esc_html__('暂无可下载资源', 'aksrt') . '
'; + } + + $buttons = ''; + foreach ($downloads as $index => $download) { + if (!is_array($download)) { + continue; + } + $url = (string) ($download['url'] ?? ''); + $label = trim((string) ($download['label'] ?? '')); + $service = function_exists('aksrt_detect_cloud_service') + ? aksrt_detect_cloud_service($url) + : ['key' => 'other', 'name' => __('本地下载', 'aksrt')]; + if ($label === '') { + $label = (string) ($service['name'] ?? __('本地下载', 'aksrt')); + } + $type = sanitize_key((string) ($download['type'] ?? 'direct')); + $has_extract = trim((string) ($download['extract_code'] ?? '')) !== ''; + $service_key = sanitize_html_class((string) ($service['key'] ?? 'other')) ?: 'other'; + + $buttons .= '
'; + $buttons .= ''; + $buttons .= '
'; + } + + return $buttons !== '' + ? '
' . $buttons . '
' + : '
' . esc_html__('暂无可下载资源', 'aksrt') . '
'; + } +} + +/** + * Purchased article card matching Zibll's paid-box structure. + */ +if (!function_exists('aksrt_get_paid_box_html')) { + function aksrt_get_paid_box_html(WP_Post $post): string { + if (!aksrt_has_paid_access($post)) { + return ''; + } + + $meta = aksrt_get_pay_meta($post); + $pay_type = (string) ($meta['pay_type'] ?? AKSRT_PAY_TYPE_NO); + if ($pay_type === AKSRT_PAY_TYPE_NO) { + return ''; + } + + $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, + ]; + $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'), + ]; + $order_type = (int) ($order_types[$pay_type] ?? 1); + $type_label = (string) ($type_labels[$pay_type] ?? __('付费内容', 'aksrt')); + $title = get_the_title($post); + $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)); + $count = $sales > 0 ? '' . esc_html(sprintf(__('已售 %s', 'aksrt'), $sales)) . '' : ''; + $order = aksrt_get_current_content_paid_order($post); + $priced = aksrt_get_user_pay_price($post); + $vip_level = max(0, min(2, (int) ($priced['vip_level'] ?? 0))); + $header = ''; + $paid_box = ''; + + if (is_array($order)) { + $order_number = (string) ($order['order_number'] ?? ''); + $paid_at = (string) ($order['paid_at'] ?? $order['created_at'] ?? ''); + $order_currency = (string) ($order['currency'] ?? ''); + $order_amount = $order['amount'] ?? '0.00'; + if ($order_currency === 'points') { + $currency_name = class_exists('\AKSRT\Settings\Settings') ? \AKSRT\Settings\Settings::get_currency_name() : __('积分', 'aksrt'); + $amount_text = aksrt_format_price($order_amount) . ' ' . $currency_name; + } else { + $cash_symbol = '¥'; + if (class_exists('\AKSRT\Settings\Settings')) { + $settings = \AKSRT\Settings\Settings::get_paid_content_settings(); + $cash_symbol = (string) ($settings['display']['currency_symbol'] ?? '¥'); + } + $amount_text = $cash_symbol . aksrt_format_price($order_amount); + } + $paid_info = '
' . esc_html__('订单号', 'aksrt') . '' . esc_html($order_number) . '
'; + if ($paid_at !== '') { + $paid_info .= '
' . esc_html__('支付时间', 'aksrt') . '' . esc_html($paid_at) . '
'; + } + $paid_info .= '
' . esc_html__('支付金额', 'aksrt') . '' . esc_html($amount_text) . '
'; + $paid_box = '
' . esc_html__('已购买', 'aksrt') . '
'; + } elseif ($vip_level > 0 || $pay_type === AKSRT_PAY_TYPE_VIP) { + $level = max(1, $vip_level); + $price_label = aksrt_get_pay_price_label($post); + $price = '
' . esc_html((string) ($price_label['text'] ?? '')) . '
'; + $paid_box = '
' . $price . '
' . esc_html(sprintf(__('VIP%d 会员免费', 'aksrt'), $level)) . '
'; + } else { + $type_label = str_replace(__('付费', 'aksrt'), __('免费', 'aksrt'), $type_label); + } + + if ($pay_type === AKSRT_PAY_TYPE_GALLERY) { + $header = aksrt_render_paid_gallery($post); + } elseif ($pay_type === AKSRT_PAY_TYPE_VIDEO) { + $header = aksrt_render_paid_video($post); + } + + $download_box = ''; + $root_data = ''; + $captcha = ''; + if ($pay_type === AKSRT_PAY_TYPE_DOWNLOAD) { + $captcha_active = function_exists('aksrt_is_captcha_enabled_for') && aksrt_is_captcha_enabled_for('download'); + $root_data = ' data-aksrt-download-box data-post-id="' . esc_attr((string) $post->ID) . '" data-captcha="' . ($captcha_active ? '1' : '0') . '"'; + $download_box = '
' . esc_html__('资源下载', 'aksrt') . '
' . aksrt_get_paid_download_actions_html($meta) . '
'; + $demo = aksrt_get_demo_link_html($post, 'but jb-yellow padding-lg'); + if ($demo !== '') { + $download_box .= '
' . $demo . '
'; + } + $download_box .= aksrt_get_pay_attributes_html($post, 'pay-attr'); + if ($captcha_active && function_exists('aksrt_render_captcha')) { + ob_start(); + aksrt_render_captcha('download'); + $captcha = '
' . (string) ob_get_clean() . '
'; + } + } + + $service = aksrt_get_pay_service_html('inline-block ml10'); + $service = $service !== '' ? '
' . $service . '
' : ''; + $title_style = $count !== '' ? ' style="padding-right:48px"' : ''; + $body_class = 'box-body relative' . ($pay_type === AKSRT_PAY_TYPE_VIDEO ? ' dplayer-featured' : ''); + + return ''; + } +} + // ============================================================ // 付费图库轮播(Swiper) // ============================================================ @@ -1313,19 +1483,25 @@ if (!function_exists('aksrt_render_paid_gallery')) { if (empty($images)) return ''; if (!aksrt_has_paid_access($post)) return ''; + $count = count($images); + $backdrop = (string) end($images); ob_start(); ?> -