搜索全站
建站 11 分钟阅读

WordPress 知识付费站:付费墙与免费试读的实现方式

免费试读做得好,转化率能翻倍;做得差,付费用户反而被拦在门外。本文给出 WordPress 付费墙的三种实现层级,附可直接粘贴的截断与放行代码。

U
UFO_PF 2018-03-05 · 阅读 11 分钟
Wo 建站
阅读提示
本文约 4,074 字,预计阅读 11 分钟。文中命令与配置均经过实测,可直接参考执行。

引言:付费墙不是”拦一段文字”那么简单

最常见的做法是”前 30% 免费,后面藏起来”。但真正上线就会遇到一堆细节:搜索引擎要不要抓到付费内容?免费用户看到的截断位置怎么定?付费用户在微信里打开要不要放行?给老用户的赠阅怎么处理?付费墙的本质是一套”内容可见性规则”,它由三个变量决定:谁在读、读的是什么、在什么入口读。把这三个变量想清楚,代码才写得对。

原理分析:三种付费墙的适用边界

类型 实现方式 优点 风险
硬付费墙 整页拦截,未登录跳转 实现最简单 SEO 拿不到内容,转化率低
计量付费墙 按篇数/天数放行 利于拉新 需记录计数,逻辑复杂
分段试读 正文按比例截断 转化最好 需处理节选与全文的 SEO 关系

对中文知识付费场景,推荐”分段试读 + 计量限制”的组合:注册用户每月免费读 3 篇,未注册用户每篇只看前 30%。这样既有拉新钩子,又不至于被白嫖。

关键点在于:给搜索引擎和付费用户返回的必须是完整正文,否则会出现”付费后仍看不到全文”或者”收录的是截断内容”这两个典型事故。做法是——判断可见性时先排除爬虫,再判断用户权限。

保姆级实操

步骤 1:确定试读的切分单位。 不要按字符数切,会切在句子中间。按段落切:取正文的前 N 个 <p>,末尾补一句引导。中文场景 N 取 3–5 段比较合适。

步骤 2:写核心的可见性判断函数。 先判爬虫(is_feed() 与常见爬虫 UA),再判是否已付费。

function mimangfei_is_crawler() {
    if ( is_feed() ) {
        return true;
    }
    $ua = isset( $_SERVER['HTTP_USER_AGENT'] ) ? $_SERVER['HTTP_USER_AGENT'] : '';
    if ( preg_match( '/bot|crawl|spider|slurp|bingpreview|facebookexternalhit/i', $ua ) ) {
        return true;
    }
    return false;
}

function mimangfei_can_read_full( $post_id, $user_id = 0 ) {
    $post_id = (int) $post_id;
    if ( ! get_post_meta( $post_id, '_is_paid', true ) ) {
        return true; // 免费内容
    }
    if ( mimangfei_is_crawler() ) {
        return true; // 让爬虫抓到全文,利于收录
    }
    $user_id = $user_id ? $user_id : get_current_user_id();
    if ( ! $user_id ) {
        return false;
    }
    // 已购买:订单记录在用户 meta 中
    $owned = (array) get_user_meta( $user_id, '_owned_posts', true );
    if ( in_array( $post_id, $owned, true ) ) {
        return true;
    }
    // 计量:本月剩余免费额度
    $quota = (int) get_user_meta( $user_id, '_monthly_quota', true );
    return $quota > 0;
}

步骤 3:在 the_content 上做截断。 优先级设高一点,避免被其他插件的内容追加逻辑覆盖。

add_filter( 'the_content', 'mimangfei_paywall_content', 5 );
function mimangfei_paywall_content( $content ) {
    if ( ! is_singular( 'post' ) || ! in_the_loop() || ! is_main_query() ) {
        return $content;
    }
    $post_id = get_the_ID();
    if ( mimangfei_can_read_full( $post_id ) ) {
        return $content;
    }

    $paragraphs = preg_split( '/(?<=<\/p>)/', $content, -1, PREG_SPLIT_NO_EMPTY );
    $free       = implode( '', array_slice( $paragraphs, 0, 4 ) );

    $notice  = '<div class="paywall-box">';
    $notice .= '<p>本文为付费内容,已读至试读部分,订阅后可继续阅读。</p>';
    $notice .= '<p><a class="paywall-btn" href="' . esc_url( home_url( '/pricing/' ) ) . '">立即订阅</a></p>';
    $notice .= '</div>';

    return $free . $notice;
}

步骤 4:扣减计量额度。 用 wp_footer 或 REST 请求触发,避免缓存把计数器弄乱。计数的 key 里带年月,天然实现按月重置。

function mimangfei_consume_quota( $user_id, $post_id ) {
    $month = current_time( 'Ym' );
    $key   = '_quota_' . $month;
    $used  = (int) get_user_meta( $user_id, $key, true );
    update_user_meta( $user_id, $key, $used + 1 );
    update_user_meta( $user_id, '_monthly_quota', max( 0, 3 - $used - 1 ) );
}

步骤 5:给付费内容加结构化标记。 用 schema.org 的 isAccessibleForFree 标注,让搜索引擎理解哪些是付费内容,避免被判为”隐藏真实内容”。

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "NewsArticle",
  "headline": "示例文章标题",
  "isAccessibleForFree": false,
  "isPartOf": {
    "@type": "Product",
    "name": "年度会员",
    "offers": {
      "@type": "Offer",
      "price": "299.00",
      "priceCurrency": "CNY"
    }
  }
}
</script>

步骤 6:处理赠阅与体验卡。 给 _owned_posts 追加 ID 即可,并在用户中心显示有效期。

步骤 7:把付费页排除出页面缓存。 同一 URL 对不同用户返回不同内容,页面缓存必须按登录态区分,否则未登录用户会看到别人的全文。

常见踩坑

  1. 付费用户看不到全文。 典型是页面缓存命中了未登录版本,按 Cookie 区分缓存即可。
  2. 收录的全是截断内容。 忘了给爬虫放行,或者用了 JS 前端截断。
  3. the_content 优先级太低。 被其他插件的”文末加版权”逻辑抢先,导致付费提示出现在全文后面。
  4. 案例: 某付费专栏把截断逻辑写在前端 JS 里,正文完整输出到 HTML。第二天内容就被整站采集走了。教训:截断必须在服务端完成。
  5. 计量额度被刷新页面刷掉。 计数写在模板里而不是独立接口,缓存层没命中时就重复计。
  6. RSS 全文泄露。 is_feed() 判断缺失,付费内容通过订阅源全量流出。
  7. 忘了标注 isAccessibleForFree。 容易被搜索引擎判定为诱导点击。

总结

付费墙的合格标准是三条:爬虫能抓全文、付费用户永远能看到全文、未付费用户在服务端就已被截断。做到这三条,再加结构化标记与按登录态区分的缓存,就是一套能长期跑的方案。更细的会员等级与权限划分见付费会员社区权限方案;订阅到期后的自动降级处理见会员续费与权限回收。

U
UFO_PF作者

专注 WordPress 生态的实战作者,内容以「可复制、可验证」为第一原则。

留下你的想法

邮箱地址不会被公开。带 * 的为必填项。