diff --git a/.gitignore b/.gitignore
index 59a5c239..ae6109b2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -36,6 +36,7 @@
!/tests/widget-system/pay-orders-filter-contract.test.cjs
!/tests/widget-system/front-submit-editor-contract.test.cjs
!/tests/widget-system/comment-smilies-removal-contract.test.cjs
+!/tests/widget-system/pay-card-contract.test.cjs
!/tests/asset-ledger/
!/tests/asset-ledger/*.php
!/tests/visual/docker-compose.zibll.yml
diff --git a/assets/css/frontend/pay-box.css b/assets/css/frontend/pay-box.css
new file mode 100644
index 00000000..6159ed25
--- /dev/null
+++ b/assets/css/frontend/pay-box.css
@@ -0,0 +1,52 @@
+/* Canonical Zibll 9.0 paid-content card shell. */
+.zib-widget.pay-box {
+ clear: both;
+ margin-bottom: 20px;
+ padding: 15px;
+ background-color: var(--main-bg-color);
+ border-radius: var(--main-radius);
+ box-shadow: 0 0 10px var(--main-shadow);
+}
+
+.pay-widget__orb {
+ position: absolute;
+ border-radius: 100%;
+}
+
+.pay-widget__orb--right {
+ top: -34%;
+ left: 75%;
+ width: 200px;
+ height: 200px;
+}
+
+.pay-widget__orb--left {
+ left: -229px;
+ width: 337px;
+ height: 305px;
+ opacity: .7;
+}
+
+.pay-thumb-placeholder {
+ position: absolute;
+ inset: 0;
+ display: grid;
+ place-items: center;
+ color: rgba(255, 255, 255, .82);
+ background: linear-gradient(135deg, #59616d, #272b31);
+ font-size: 40px;
+}
+
+.pay-box .pay-attr {
+ margin-top: 10px;
+}
+
+.pay-box .pay-attr-item {
+ display: flex;
+ justify-content: space-between;
+ gap: 12px;
+}
+
+.pay-box .muted-2-color + .muted-2-color {
+ margin-left: 10px;
+}
diff --git a/assets/css/frontend/single-post/cover.css b/assets/css/frontend/single-post/cover.css
index 5a24d64c..96bece19 100644
--- a/assets/css/frontend/single-post/cover.css
+++ b/assets/css/frontend/single-post/cover.css
@@ -226,18 +226,6 @@
.single-head-cover .cover-meta.single-metabox {
display: flex; flex-wrap: wrap; gap: 8px 12px; font-size: 12px; color: var(--color-ink-4);
}
-/* Pay box root — legacy #posts-pay.aksrt-panel.pay-box */
-#posts-pay.aksrt-panel,
-#posts-pay.pay-box,
-.aksrt-single-pay-box {
- margin: 16px 0 20px;
-}
-#posts-pay .aksrt-paywall,
-.aksrt-single-pay-box .pay-box,
-.aksrt-single-pay-box .aksrt-paywall {
- margin: 0;
-}
-
/* Cover meta pills + title-h-left */
.single-cover-con .article-title.title-h-left,
.single-cover .article-title.title-h-left {
diff --git a/assets/css/frontend/single-post/hidden-content.css b/assets/css/frontend/single-post/hidden-content.css
index f96e8e74..f6db2f4d 100644
--- a/assets/css/frontend/single-post/hidden-content.css
+++ b/assets/css/frontend/single-post/hidden-content.css
@@ -119,117 +119,5 @@
opacity: .85;
}
-/* ---- Paid fragment purchase card ---- */
-.aksrt-paywall {
- position: relative;
- margin: 24px 0;
-}
-
-.aksrt-paywall__card {
- position: relative;
- z-index: 0;
- display: grid;
- grid-template-columns: auto minmax(0, 1fr) auto;
- align-items: center;
- gap: 18px;
- padding: 22px 24px;
- background: linear-gradient(135deg, var(--surface-1, #fff) 0%, var(--surface-2, #f8fafc) 100%);
- border: 1px solid var(--color-line, #e5e7eb);
- border-radius: var(--radius-lg, 14px);
- box-shadow: 0 10px 28px rgba(15, 23, 42, .08);
-}
-
-.aksrt-paywall--fragment .aksrt-paywall__card {
- border-style: dashed;
- border-color: color-mix(in srgb, var(--color-primary, #f04494) 42%, var(--color-line, #e5e7eb));
-}
-
-.aksrt-paywall__icon {
- display: grid;
- place-items: center;
- width: 44px;
- height: 44px;
- flex: 0 0 auto;
- border-radius: 50%;
- color: var(--color-primary, #f04494);
- background: color-mix(in srgb, var(--color-primary, #f04494) 12%, transparent);
- font-size: 20px;
-}
-
-.aksrt-paywall__body { min-width: 0; }
-
-.aksrt-paywall__title {
- margin: 0 0 4px;
- color: var(--color-ink, #15171a);
- font-size: 17px;
- font-weight: 700;
-}
-
-.aksrt-paywall__desc {
- margin: 0;
- color: var(--color-ink-3, #525866);
- font-size: 14px;
- line-height: 1.6;
-}
-
-.aksrt-paywall__price { margin-top: 8px; }
-.aksrt-paywall__price-amount { color: var(--color-error, #ef4444); font-size: 20px; font-weight: 700; }
-.aksrt-paywall__price-member { color: var(--color-primary, #f04494); font-size: 16px; font-weight: 700; }
-.aksrt-paywall__price-free { color: var(--color-success, #10b981); font-size: 16px; font-weight: 700; }
-.aksrt-paywall__price-original { margin-left: 8px; color: var(--color-ink-4, #8a8f98); font-size: 13px; text-decoration: line-through; }
-
-.aksrt-paywall__actions {
- position: relative;
- z-index: 1;
- display: flex;
- flex: 0 0 auto;
- flex-direction: column;
- align-items: center;
- gap: 8px;
- text-align: center;
-}
-
-.aksrt-paywall__btn {
- display: inline-flex;
- align-items: center;
- justify-content: center;
- gap: 8px;
- min-height: 40px;
- white-space: nowrap;
- cursor: pointer;
-}
-
-.aksrt-paywall__btn:focus-visible,
-.aksrt-paywall__login-link:focus-visible {
- outline: 3px solid color-mix(in srgb, var(--color-primary, #f04494) 35%, transparent);
- outline-offset: 3px;
-}
-
-.aksrt-paywall__hint,
-.aksrt-paywall__login-link {
- margin: 0;
- color: var(--color-ink-4, #8a8f98);
- font-size: 12px;
- line-height: 1.5;
-}
-
-.aksrt-paywall__login-link { text-decoration: underline; }
-.aksrt-gallery-locked { filter: blur(8px); pointer-events: none; user-select: none; }
.aksrt-paywall-preview { margin: 0 0 16px; padding: 14px 16px; border-left: 3px solid var(--color-primary, #f04494); background: var(--surface-2, #f8fafc); border-radius: 0 10px 10px 0; color: var(--color-ink-2, #374151); line-height: 1.7; }
.aksrt-paywall-preview p:last-child { margin-bottom: 0; }
-.aksrt-paid-gallery__overlay .but, .aksrt-paid-video__placeholder .but { margin-top: 10px; }
-
-@media (max-width: 680px) {
- .aksrt-paywall__card {
- grid-template-columns: auto minmax(0, 1fr);
- padding: 18px;
- }
-
- .aksrt-paywall__actions {
- grid-column: 1 / -1;
- align-items: stretch;
- width: 100%;
- }
-
- .aksrt-paywall__btn { width: 100%; }
-}
diff --git a/assets/widgets/frontend.css b/assets/widgets/frontend.css
index fbaf5b20..ace4b740 100644
--- a/assets/widgets/frontend.css
+++ b/assets/widgets/frontend.css
@@ -125,7 +125,6 @@
.aksrt-term-aggregation { display: grid; grid-template-columns: repeat(2,minmax(0,1fr)); gap: 15px; }.aksrt-term-aggregation__card { padding: 15px; border-radius: var(--aksrt-widget-radius); background: var(--main-bg-color,#fff); box-shadow: 0 0 10px var(--main-shadow,rgba(116,116,116,.08)); }.aksrt-term-aggregation header a { display: flex; justify-content: space-between; }.aksrt-term-aggregation header span { color: var(--muted-color,#8a8c98); font-size: 13px; }.aksrt-term-aggregation ul { display: grid; gap: 10px; margin: 14px 0 0; padding: 0; list-style: none; }.aksrt-term-aggregation li a { display: flex; align-items: center; gap: 10px; }.aksrt-term-aggregation li img { width: 55px; height: 42px; border-radius: 6px; object-fit: cover; }
.aksrt-widget-items { position: relative; min-height: 40px; }.aksrt-widget-items.is-loading { opacity: .72; pointer-events: none; }.aksrt-widget-skeleton { height: 180px; border-radius: 10px; background: linear-gradient(90deg,rgba(127,127,127,.08),rgba(127,127,127,.17),rgba(127,127,127,.08)); background-size: 200% 100%; animation: aksrt-shimmer 1.2s infinite; }.aksrt-widget-load-more { display: block; min-width: 120px; margin: 18px auto 0; padding: 9px 18px; border: 0; border-radius: 999px; background: rgba(127,127,127,.12); color: inherit; cursor: pointer; }.aksrt-widget-pagination { display:flex;justify-content:center;gap:6px;margin-top:18px; }.aksrt-widget-pagination button { min-width:34px;height:34px;border:0;border-radius:8px;background:rgba(127,127,127,.1);color:inherit;cursor:pointer; }.aksrt-widget-pagination button[aria-current="page"] { background:var(--focus-color,#ff5473);color:#fff; }@keyframes aksrt-shimmer { to { background-position: -200% 0; } }
.aksrt-widget-oneline .swiper-slide { width: min(300px,78vw); }.aksrt-widget-tabs__nav { display: flex; gap: 8px; margin-bottom: 15px; overflow-x: auto; }.aksrt-widget-tabs__nav button { flex: none; padding: 7px 14px; border: 0; border-radius: 999px; background: rgba(127,127,127,.1); color: inherit; cursor: pointer; }.aksrt-widget-tabs__nav button[aria-selected="true"] { background: var(--focus-color,#ff5473); color: #fff; }
-.aksrt-post-pay { padding: 20px; border-radius: var(--aksrt-widget-radius); text-align: center; }.aksrt-post-pay.is-authorized { background: rgba(34,197,94,.1); }.aksrt-post-pay > i { display: block; margin-bottom: 9px; color: #22c55e; font-size: 30px; }.aksrt-post-pay p { color: var(--muted-color,#8a8c98); }
.aksrt-pricing-card { position: relative; display: flex; flex-direction: column; padding: 20px; border: 1px solid var(--main-border-color,rgba(127,127,127,.13)); border-radius: var(--main-radius,8px); background: var(--main-bg-color,#fff); box-shadow: 0 0 10px var(--main-shadow,rgba(116,116,116,.08)); }.aksrt-pricing-card.is-featured { border-color: var(--focus-color,#ff5473); }.aksrt-pricing-card__badge { position: absolute; top: 12px; right: 12px; padding: 4px 9px; border-radius: 999px; background: var(--focus-color,#ff5473); color: #fff; font-size: 12px; }.aksrt-pricing-card h3 { margin: 0 0 12px; }.aksrt-pricing-card__price { font-size: 34px; font-weight: 800; }.aksrt-pricing-card p { color: var(--muted-color,#8a8c98); }.aksrt-pricing-card ul { flex: 1; display: grid; gap: 9px; margin: 15px 0 22px; padding: 0; list-style: none; }.aksrt-pricing-card li i { margin-right: 8px; color: #22c55e; }
.aksrt-reviews .swiper-slide { width: min(390px,86vw); height: auto; }.aksrt-review-card { height: 100%; padding: 20px; border-radius: var(--main-radius,8px); background: var(--main-bg-color,#fff); box-shadow: 0 0 10px var(--main-shadow,rgba(116,116,116,.08)); }.aksrt-review-card__rating { color: #f6b73c; }.aksrt-review-card blockquote { min-height: 80px; margin: 15px 0; line-height: 1.75; }.aksrt-review-card footer { display: flex; align-items: center; gap: 11px; }.aksrt-review-card__avatar { width: 44px; height: 44px; border-radius: 50%; object-fit: cover; }.aksrt-review-card footer span { display: flex; flex-direction: column; }.aksrt-review-card footer small { color: var(--muted-color,#8a8c98); }
diff --git a/inc/core-hooks/part-3.php b/inc/core-hooks/part-3.php
index 9e20d0c3..59879533 100644
--- a/inc/core-hooks/part-3.php
+++ b/inc/core-hooks/part-3.php
@@ -240,15 +240,10 @@ add_action('init', function () {
// ============================================================
/**
- * 未付费内容:替换为付费墙。
+ * 未购买的图库/视频正文只保留公开预览;购买卡由 single 模板渲染。
*/
add_filter('the_content', 'aksrt_paid_content_filter', 20);
-/**
- * 已付费内容增强:追加图库轮播/视频播放器。
- */
-add_filter('the_content', 'aksrt_paid_content_enhance', 25);
-
/**
* 付费/资源文章使用统一的 single.php。
*/
diff --git a/inc/helpers/aksrt-pay.php b/inc/helpers/aksrt-pay.php
index 7d734b30..d7d2552e 100644
--- a/inc/helpers/aksrt-pay.php
+++ b/inc/helpers/aksrt-pay.php
@@ -157,25 +157,6 @@ if (!function_exists('aksrt_get_pay_meta')) {
if (!function_exists('aksrt_default_pay_meta')) {
function aksrt_default_pay_meta(): array {
return \AKSRT\Core\DomainModel::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' => '',
- ];
}
}
@@ -1195,101 +1176,127 @@ if (!function_exists('aksrt_get_pay_attributes_html')) {
// ============================================================
if (!function_exists('aksrt_get_paywall_html')) {
- function aksrt_get_paywall_html(WP_Post $post, bool $is_fragment = false): string {
+ 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 = $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)
- ? ($priced['amount'] ?? ($pay_modo === 'points' ? 0 : '0.00'))
- : ($pay_modo === 'points' ? (int) ($meta['points_price'] ?? 0) : (string) ($meta['price'] ?? '0.00'));
- $price_minor = aksrt_asset_price_minor($price, $pay_modo);
- $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'] ?? '¥');
- }
- $price_text = $pay_modo === 'points'
- ? (aksrt_format_price($price) . ' ' . $currency)
- : ($cash_symbol . aksrt_format_price($price));
+ $pay_type = (string) ($meta['pay_type'] ?? AKSRT_PAY_TYPE_NO);
+ if ($pay_type === AKSRT_PAY_TYPE_NO) return '';
- $type_labels = [
- AKSRT_PAY_TYPE_READING => ['icon' => 'fa-lock', 'label' => __('付费阅读', 'aksrt'), 'desc' => __('付费后即可查看完整内容', 'aksrt')],
- AKSRT_PAY_TYPE_GALLERY => ['icon' => 'fa-image', '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];
- if ($is_fragment && $pay_type === AKSRT_PAY_TYPE_READING) {
- $type_info['desc'] = __('付费后即可查看此片段内容', 'aksrt');
- }
- $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 = $pay_modo === 'points'
- ? max(0, (int) ($meta['original_price'] ?? 0))
- : (string) ($meta['original_price'] ?? '0.00');
- $original_minor = aksrt_asset_price_minor($original_price, $pay_modo);
- $has_original = $original_minor > 0 && $original_minor > $price_minor;
- $pay_limit = (int) ($meta['pay_limit'] ?? 0);
+ $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;
- $vip_url = function_exists('aksrt_get_user_center_tab_url')
- ? aksrt_get_user_center_tab_url('credits')
- : (function_exists('aksrt_get_user_center_url') ? aksrt_get_user_center_url() : home_url('/'));
+ $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
+ ? '
- -
-', - '
' . esc_html($message) . ' ', - $paywall - ); - } - return $paywall; - } - - return '