资讯动态

WordPress微信公众号扫码解锁隐藏内容插件实现指南

发布时间:2026/9/15 17:24:51 来源:尧图企业网站定制
简介在内容保护与粉丝沉淀的平衡中隐藏内容解锁是一种轻量化解决方案。其核心原理不是简单的前端折叠而是通过后端截断与一次性票据机制确保未解锁用户无法获取真实内容。基于WordPress的短代码体系结合微信公众号扫码回调可以构建完整的解密链路后端生成临时票据前端轮询解锁状态用户扫码关注后自动绑定openid并刷新页面。该方案在保护代码、下载链接等敏感信息的同时显著降低跳出率适合教程站、素材站等场景。短代码注册、REST API设计、微信公众号事件推送对接以及缓存、限流等上线细节构成一套可落地的工程实践为个人开发者和内容运营者提供了一条高效获取精准粉丝的路径。1. 隐藏内容引流插件到底在解决什么很多运营者最初理解“隐藏内容引流”就是把整篇文章的后半段藏起来访客关注公众号才能看全文。实际跑一阵就会发现跳出率明显上涨因为用户为了看一篇文章付出的成本太高尤其是搜索引擎访客连内容质量都没确认就被一道墙挡住。更合理的做法是只隐藏“下载地址、关键代码片段、资源链接”这几个独立区块访客扫码关注公众号后即时解锁解锁状态在本地或账号维度保留一段时间。这个标题要讲的就是在 WordPress 里把正文按区块隐藏、用微信公众号扫码验证作为解锁条件的插件实现思路。适合教程站、源码分享站、素材类站点以及不想做完整付费墙、只想靠内容沉淀公众号粉丝的个人开发者。2. 隐藏内容选型前端折叠、后端截断与公众号验证组合写这类插件第一件事不是写短代码而是先决定“隐藏在哪一层”。隐藏层级直接决定插件要承担的职责范围也决定了会不会被访客一行代码绕过。2.1 前端隐藏与后端截断的差距前端隐藏是最常见的错误实现用一段 CSS 把某个 div 的max-height或display控制住再让用户扫码后调 JS 移除样式。实现成本很低但内容完整存在于 HTML 源码里访客按 F12 删掉一条 class 就能看光。更麻烦的是这种做法对爬虫和正文提取工具不友好内容重复收录和泄露的风险都不可控。后端截断的思路是“未解锁时压根不输出内容”。WordPress 里用短代码做这个很自然短代码回调先判断当前访客有没有解锁权限有权限就返回do_shortcode($content)处理过的正文没有权限就返回一个空容器或占位提示让前端脚本去渲染二维码和状态轮询。截断层级决定了短代码返回值add_shortcode(unlock, function ($atts, $content null) { $atts shortcode_atts([ title 关注公众号后查看, tips 扫码关注获取完整代码和下载地址, ], $atts, unlock); $post_id get_the_ID(); if (wc_unlock_check_status($post_id)) { return do_shortcode($content); } return div classwc-unlock-box>function wc_unlock_check_status($post_id) { $user_id get_current_user_id(); if ($user_id 0) { $list get_user_meta($user_id, wc_unlocked_posts, true); $list is_array($list) ? $list : []; return in_array($post_id, $list, true); } $cookie isset($_COOKIE[wc_unlock]) ? json_decode(stripslashes($_COOKIE[wc_unlock]), true) : []; return is_array($cookie) in_array($post_id, $cookie); } add_action(save_post, function ($post_id) { if (has_shortcode(get_post_field(post_content, $post_id), unlock)) { update_post_meta($post_id, _wc_has_unlock, 1); } else { delete_post_meta($post_id, _wc_has_unlock); } });wc_unlock_check_status先取当前用户 ID登录用户读wc_unlocked_posts这个 meta 数组未登录则解析wc_unlockcookie。save_post钩子会在保存文章时扫描内容里是否包含unlock短代码并写入_wc_has_unlock标记后面做缓存排除和文章列表过滤时会用到这个标记。3.2 用 REST 接口发一次性二维码票据票据是整个解锁链路的关键。前端向 REST 接口申请一个票据插件生成随机 UUID 并用transient缓存 10 分钟访客扫码后公众号回调把 openid 写进这条 transient轮询接口发现 openid 非空即视为解锁。票据是一次性的过期自动失效。add_action(rest_api_init, function () { register_rest_route(wc-unlock/v1, /ticket, [ methods POST, callback wc_unlock_generate_ticket, permission_callback __return_true, ]); register_rest_route(wc-unlock/v1, /check, [ methods GET, callback wc_unlock_check_ticket, permission_callback __return_true, ]); }); function wc_unlock_generate_ticket($request) { $post_id absint($request-get_param(post_id)); if (!$post_id || !get_post($post_id)) { return new WP_Error(invalid_post, 文章不存在, [status 404]); } $ticket wp_generate_uuid4(); set_transient(wc_unlock_ticket_ . $ticket, [ post_id $post_id, openid , ], 10 * MINUTE_IN_SECONDS); return [ticket $ticket]; } function wc_unlock_check_ticket($request) { $ticket sanitize_key($request-get_param(ticket)); $data get_transient(wc_unlock_ticket_ . $ticket); if ($data !empty($data[openid])) { return [ unlocked true, post_id $data[post_id], ]; } return [unlocked false]; }permission_callback设为__return_true因为前端未登录访客也要能申请票据。票暂时只存post_id和openid两个字段10 分钟过期。check接口返回unlockedfalse时前端会继续轮询返回true则刷新页面。3.3 前端渲染二维码并轮询解锁状态前端脚本负责三件事拿到票据、渲染二维码、轮询解锁结果。二维码渲染可以使用现成的 JavaScript 二维码库也可由公众号接口返回的二维码图片地址直接展示。为了不让二维码库体积拖慢页面我偏向在票据生成后调用公众号带参数二维码接口返回图片 URL前端只负责放img标签。document.querySelectorAll(.wc-unlock-box).forEach(function (box) { const postId box.dataset.post; fetch(/wp-json/wc-unlock/v1/ticket, { method: POST, headers: {Content-Type: application/json}, body: JSON.stringify({post_id: postId}) }) .then(response response.json()) .then(data { if (!data.ticket) return; box.innerHTML p请扫码关注公众号/pimg src/?wc_qr data.ticket altqr; pollStatus(data.ticket); }); function pollStatus(ticket) { const timer setInterval(async () { const res await fetch(/wp-json/wc-unlock/v1/check?ticket ticket); const result await res.json(); if (result.unlocked) { clearInterval(timer); location.reload(); } }, 2000); } });postId从短代码渲染出来的>add_action(init, function () { if (!isset($_GET[wc_wechat])) { return; } $token get_option(wc_unlock_wechat_token, ); $signature isset($_GET[signature]) ? $_GET[signature] : ; $timestamp isset($_GET[timestamp]) ? $_GET[timestamp] : ; $nonce isset($_GET[nonce]) ? $_GET[nonce] : ; $tmp_arr [$token, $timestamp, $nonce]; sort($tmp_arr, SORT_STRING); if (sha1(implode($tmp_arr)) ! $signature) { exit(signature error); } if ($_SERVER[REQUEST_METHOD] GET) { exit($_GET[echostr]); } $xml file_get_contents(php://input); $msg simplexml_load_string($xml, SimpleXMLElement, LIBXML_NOCDATA); if ($msg-MsgType event $msg-Event subscribe) { $event_key (string) $msg-EventKey; $scene str_replace(qrscene_, , $event_key); wc_unlock_bind_openid($scene, (string) $msg-FromUserName); } exit(success); }); function wc_unlock_bind_openid($ticket, $openid) { if (!$ticket || !$openid) return; $data get_transient(wc_unlock_ticket_ . sanitize_key($ticket)); if ($data) { $data[openid] $openid; set_transient(wc_unlock_ticket_ . sanitize_key($ticket), $data, 10 * MINUTE_IN_SECONDS); } }首次关注公众号的用户扫码后微信推送的EventKey形如qrscene_你的ticket已经关注过的老用户再扫同一个码收到的是Event等于scan的事件EventKey直接是场景值不带qrscene_前缀。两种事件在真实运营中都会遇到函数里做了一次str_replace归一化处理转发到同一个绑定函数。4.3 生成带参数二维码的接口调用带参数二维码需要先拿到全局 access_token再调用二维码创建接口。access_token 有效期两小时不要每次请求都重新获取用transient缓存起来更合理。先看生成二维码图片地址的完整流程# 第一步获取 access_token curl -s https://api.weixin.qq.com/cgi-bin/token?grant_typeclient_credentialappid你的APPIDsecret你的SECRET # 第二步创建临时二维码scene_str 填前面生成的一次性票据 curl -s -X POST https://api.weixin.qq.com/cgi-bin/qrcode/create?access_tokenACCESS_TOKEN \ -d {expire_seconds:600,action_name:QR_STR_SCENE,action_info:{scene:{scene_str:票据值}}}第二次请求的响应里包含ticket和url两个字段url就是二维码图片地址前端直接把img的src指向它。考虑到 expire_seconds 是 600 秒而transient也设置了 10 分钟两边的时间要匹配不然会出现二维码已失效但票据仍可查询的情况。这个url参数不需要再额外调用其他接口它本身就能渲染出有效二维码。5. 上线前收口缓存、限流与链路验证插件在本地跑通不代表生产环境能用。真实站点最常见的三个问题分别是页面缓存把隐藏内容提前渲染、接口被刷、以及轮询链路里某个节点静默失败。上线前需要收口这三件事。5.1 对含隐藏内容的页面禁用页面缓存文章内容被页面缓存插件缓存后访客扫码解锁不会触发后端重新渲染页面可能仍然显示隐藏状态或者直接暴露不该显示的内容。一篇文章是否含unlock短代码在save_post时已经写到_wc_has_unlock字段模板加载时直接判断即可add_action(wp, function () { if (is_singular() get_post_meta(get_the_ID(), _wc_has_unlock, true)) { define(DONOTCACHEPAGE, true); nocache_headers(); } });DONOTCACHEPAGE是一个约定俗成的常量绝大多数主流页面缓存插件都会在渲染前检查它。nocache_headers()负责输出Cache-Control: no-cache响应头双保险防止 CDN 层拦截。5.2 获取票据接口限流/ticket接口不需要登录如果不加限制会被脚本批量调用占满 transient 记录。简单的限流方案用 IP 维度加计数器function wc_unlock_generate_ticket($request) { $ip isset($_SERVER[REMOTE_ADDR]) ? $_SERVER[REMOTE_ADDR] : unknown; $count (int) get_transient(wc_unlock_limit_ . md5($ip)); if ($count 10) { return new WP_Error(too_many, 请求过于频繁请稍后再试, [status 429]); } set_transient(wc_unlock_limit_ . md5($ip), $count 1, HOUR_IN_SECONDS); // 原有票据生成逻辑不变 }transient的有效期设 1 小时超过 10 次请求直接返回 429 状态码。这个阈值可以根据文章页的日均流量调整一般 10 次对正常访客完全够用又能挡住无差别遍历的脚本。5.3 用一条命令验证票据链路给文章页配好短代码后先用命令行模拟前端请求再扫二维码观察回调curl -s -X POST https://你的域名/wp-json/wc-unlock/v1/ticket \ -H Content-Type: application/json \ --data {post_id:文章ID}命令返回的 JSON 里应该包含ticket字段。拿到票据后在公众号后台用测试二维码扫码关注观察服务器日志里是否出现带qrscene_前缀的事件推送再调用check接口确认unlocked是否变成true。如果一直停在false优先排查公众号后台的服务器配置是否保存成功、Token 是否和wp_options里存的完全一致再确认服务器没有屏蔽微信官方回调 IP。把公众号后台请求地址切回测试接口手机扫码盯着 PHP 错误日志看 30 秒链路通了再上生产。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价