资讯动态

公众号约玩树洞陪聊系统源码拆解:匹配与支付回调实战

发布时间:2026/10/26 1:10:17 来源:尧图企业网站定制
简介一套面向公众号约玩、树洞与陪聊业务场景的开源版源码适合具有一定PHP和前端基础的开发者或运营者快速搭建属于自己的陪聊服务平台。整个资源包约28.49MB共2000个文件其中1291个JavaScript文件、227个HTML页面、150个Markdown文档、146个JSON配置、131个CSS样式等分别对应前端交互逻辑、页面结构、部署教程、系统配置和界面外观目录清晰便于按需查阅。功能层面源码支持钱包预充值、店员收藏、礼物打赏并内置上下级分销与多分销返点开通制度可灵活配置推广体系。与市场上加密的流程版本不同此版本为开源未加密版本前端采用H5编译形式更适合学习研究或直接改造上线。包内附带详细Markdown部署教程能有效降低公众号环境下的搭建难度。目前已有77人浏览学习适合作为从零部署同类系统的参考。1. 公众号约玩树洞系统陪聊源码拆完之后真正值钱的是“匹配与结算”公众号约玩树洞系统说白了就是一套把“倾诉需求”变成“付费陪聊订单”的微信生态业务源码。很多人第一次接触陪聊源码以为它就是一个聊天窗口实际拆开这份带教程的源码才发现它的核心精力全花在用户匹配、订单状态和支付回调这三件事上聊天反而不复杂。它能解决什么问题对运营方来说是把公众号粉丝转成付费用户的完整闭环——用户进公众号匿名倾诉系统匹配陪聊师按分钟计费微信支付自动结算。适合谁适合手里有公众号流量、想快速上线付费陪聊或树洞业务的技术运营也适合专门研究微信 JSAPI 支付回调的开发者。这份源码带教程从公众号后台配置到数据库初始化都有覆盖下面按我的拆解顺序来讲。2. 架构拆解消息路由、数据表与状态机2.1 微信公众号开发者模式与消息流转链路先说大框架。微信公众号不是服务器直接收到用户消息而是用户先给微信服务器发消息微信再把这条消息 POST 到你在公众号后台配置的 URL 上。URL 一般指向源码里的 index.php这个文件就是整套系统的入口。整个链路是用户发文本 → 微信服务器 → 你的 Nginx → PHP 入口index.php→ 业务分发 → 数据库读写 → 被动回复 XML 报文 → 微信服务器 → 用户收到消息这里最关键的验证是第一次配置 URL 时微信会带 signature、timestamp、nonce、echostr 四个参数 GET 请求你的 URL。你的程序要把 token、timestamp、nonce 按字典序排序拼成一个字符串做 SHA1 加密对比签名一致后才返回 echostr。源码里的 checkSignature() 方法干的就是这件事?php // 入口文件 index.php 里的签名验证 define(TOKEN, your_token_here); // 与公众号后台填写的 Token 必须一致 function checkSignature() { $signature $_GET[signature] ?? ; $timestamp $_GET[timestamp] ?? ; $nonce $_GET[nonce] ?? ; // 微信要求 token、timestamp、nonce 三个参数字典序排序 $tmpArr [TOKEN, $timestamp, $nonce]; sort($tmpArr, SORT_STRING); $tmpStr implode($tmpArr); $tmpStr sha1($tmpStr); return $tmpStr $signature; } // 只有 echo 验证请求才走这里 if (isset($_GET[echostr])) { if (checkSignature()) { echo $_GET[echostr]; exit; } exit(signature error); } // 业务消息从 php://input 读原生 XML $postStr file_get_contents(php://input);逻辑说明TOKEN 常量要和公众号后台“服务器配置”里填的 Token 一字不差这是签名能对上的前提。排序用的是 SORT_STRING千万不要用默认的 SORT_REGULAR否则纯数字场景下排序结果可能不一致。签名验证通过后echostr 原样返回给微信验证才能通过。之后的业务消息和事件推送都走 POST从 php://input 读原始 XML用 simplexml_load_string 解析。参数说明里有个细节很多人一开始会把 $_GET[echostr] 的判断放在签名验证之前看起来没毛病但这样等于把验证逻辑架空了——微信要求是验签通过才回显 echostr顺序不能反。2.2 数据表设计三张核心表撑起整个业务这套源码的数据库一般就三张核心表加几张辅助表。我拆的时候最先看的就是表结构因为业务逻辑到最后全是查表、改表。按常见版本把表结构列出来你会发现在这三张表里状态字段几乎是全部业务的关键。用户表 t_user字段名类型说明openidvarchar(64)微信用户唯一标识主键nicknamevarchar(50)用户的昵称授权后保存avatarvarchar(255)头像 URLroletinyint0普通用户1陪聊师statustinyint0正常1封禁balancedecimal(10,2)陪聊师的余额结算时用树洞帖子表 t_post字段名类型说明idint(11)自增主键openidvarchar(64)发帖人但对外匿名展示contenttext倾诉内容create_timeint(11)发布时间戳statustinyint0待捞取1已回复2已下架订单表 t_order字段名类型说明order_snvarchar(32)订单号规则一般是日期随机串openidvarchar(64)下单用户server_idvarchar(64)陪聊师的 openiddurationint(11)购买时长单位分钟amountdecimal(10,2)支付金额单位元statustinyint0待支付1已支付2服务中3已完成4已退款create_timeint(11)下单时间为什么要强调这三张表因为这套业务的闭环就是“用户 → 树洞帖子 → 陪聊订单 → 结算”。用户先通过树洞表达诉求系统识别到用户有继续聊的需求后推陪聊师名片用户下单陪聊师提供服务订单从待支付一路走到已完成。做二次开发改的无非就是这三个状态机。还有一点容易忽略t_post 的 content 字段在导入数据库前要把表的默认字符集确认一下常见做法是统一用 utf8mb4 而不是 utf8否则用户发个 emoji 表情就直接插入失败树洞帖子根本存不进去。2.3 状态机从匿名倾诉到付费陪聊的完整流程状态机是这套源码里最值得花时间理解的部分。我从源码里捋出来的完整流程是这样的普通用户进来发#树洞开头的文本t_post 插入一条 status0 的帖子系统回复“已匿名丢进树洞”。陪聊师端通常是另一个公众号或后台可以捞取帖子捞取后 t_post.status 变成 1同时给用户推送一条客服消息内容是陪聊师的介绍卡片。用户如果想继续就进入下单流程创建 t_order 订单status0 待支付用户点击菜单或链接跳转微信支付。支付成功后微信回调你的 URL 更新订单为 1这时陪聊师就可以和用户正常对话了服务结束后订单状态置为 3陪聊师余额按比例增加。这个流程里最容易写乱的是订单状态。很多没经验的开发者会把“已支付”和“服务中”合并成一个字段结果退款时根本没法判断到底退到哪一步。源码里把 status 拆成 0 到 4 五个状态每个状态对应一个时间点退款逻辑就只在 status 为 0 或者 1 的时候允许2 表示服务中要双方协商这个设计是合理的。提示不要把服务状态和支付状态混在一个字段里。支付状态关心的是钱服务状态关心的是时间两个维度分开才能扛住售后纠纷。3. 核心代码实战消息分发、匿名树洞与支付回调3.1 消息分发一个 switch 撑起全部业务公众号源码没有路由框架业务分发全靠一个文本命令前缀。我拆到的这套源码在 index.php 里维护了一个 dispatchMessage() 方法所有从微信过来的消息都先进这里按 MsgType 分一次再按 Content 的前缀分一次?php // 消息分发器所有业务从这里进入 function dispatchMessage($postObj) { $openid $postObj-FromUserName; $msgType $postObj-MsgType; $content trim($postObj-Content ?? ); // 事件消息单独处理关注、扫描、点击菜单 if ($msgType event) { $eventKey $postObj-EventKey ?? ; return handleEvent($postObj, $openid, $eventKey); } // 文本消息按命令前缀分发 if ($msgType text) { // 中文命令前缀统一先 urldecode避免微信部分场景传 URL 编码 $content urldecode($content); if (mb_strpos($content, #树洞) 0) { return publishPost($openid, mb_substr($content, 5, null, utf-8)); } if (mb_strpos($content, #捞) 0) { return fetchPost($openid); } if (mb_strpos($content, 下单) 0) { return createOrder($openid, $content); } // 默认走陪聊聊天 return chatMessage($openid, $content); } return replyText($openid, 我只能处理文本消息哦); }逻辑说明事件消息和文本消息分开是微信的硬性要求event 的报文结构跟 text 不一样没有 Content 字段所以必须先判断 MsgType 再往下走。命令前缀这套设计很朴素但很稳树洞、捞帖子、下单各自是独立函数出问题互不影响。参数说明这里必须用 mb_* 系列函数mb_substr($content, 5, null, utf-8)的 offset 是按字符数算的“#树洞”加一个空格正好 5 个字符截出来就是正文。如果换成 PHP 原生的 substr#树洞 在 UTF-8 下占 7 个字节offset5 会把“树”字截掉一半后面做长度判断必挂。这个细节对应避坑章节 5.4是这种中文命令源码最常见的翻车点。3.2 匿名树洞发布、捞取与回复树洞的核心是匿名。源码里发布帖子的函数只存 openid 不存昵称对外展示时统一用“树洞用户”加 ID 尾巴这是保护隐私最基本的一层。发布代码如下?php // 发布树洞帖子 function publishPost($openid, $content) { // 最少 2 个字符防刷屏 if (mb_strlen($content, utf-8) 2) { return replyText($openid, 多说一点嘛至少 2 个字); } // 插入帖子表openid 只用于后台追溯前端匿名 $sql INSERT INTO t_post (openid, content, create_time, status) VALUES ($openid, . addslashes($content) . , . time() . , 0); $db-exec($sql); // 把自增 ID 回给用户后续可用来被捞取 return replyText($openid, 已匿名丢进树洞 ID: . $db-lastInsertId() . 等有缘人来捞); }逻辑说明2 个字的下限判断是防止刷屏这个阈值可以在 config.php 里改成 5 或者 10。addslashes 是简单转义生产环境建议改成 PDO 预处理树洞这种 UGC 场景防注入和防 XSS 一样重要。插入成功后把自增 ID 回给用户用户后续说“捞 123”就是捞取这个 ID 的帖子。捞取的功能在陪聊师端触发。陪聊师发送#捞 123系统把帖子 status 置为 1并把陪聊师信息推给发帖用户。这里要注意捞取不是直接把两个用户拉到一个群里而是通过微信的客服消息接口给发帖用户推一条带链接的消息链接指向陪聊师的介绍页用户点进去决定要不要下单。为什么这么设计因为直接推送好友名片在微信规范里是受限的而且用户隐私也保不住。3.3 陪聊订单JSAPI 支付与回调验签这是整套源码含金量最高的部分。微信 JSAPI 支付要求在公众号内通过网页授权拿到用户 openid然后用 openid 发起统一下单。源码里的创建订单逻辑大致是这样的?php // 创建陪聊订单统一下单后返回 JSAPI 参数 function createOrder($openid, $duration) { // 1. 组装订单号日期 6 位随机数 $orderSn date(YmdHis) . rand(100000, 999999); // 2. 按单价计算金额比如 5 元/分钟 $amount intval($duration) * 5 * 100; // 单位分 // 3. 写入待支付订单 $sql INSERT INTO t_order (order_sn, openid, amount, status, create_time) VALUES ($orderSn, $openid, $amount, 0, . time() . ); $db-exec($sql); // 4. 组装统一下单参数签名后 POST 到微信支付 $params [ appid APPID, mch_id MCH_ID, nonce_str generateNonceStr(), body 陪聊服务, out_trade_no $orderSn, total_fee $amount, spbill_create_ip $_SERVER[REMOTE_ADDR], notify_url https://你的域名/pay_notify.php, trade_type JSAPI, openid $openid ]; // 生成支付签名并 POST 到微信 $params[sign] makeSign($params, API_KEY); $result postXml(https://api.mch.weixin.qq.com/pay/unifiedorder, $params); // 5. 返回给前端 JS 用的调起支付参数 return json_encode([ order_sn $orderSn, js_params buildJsApiParams($result[prepay_id]) ]); }逻辑说明金额在数据库里存的是元但微信支付接口要求单位是分intval($duration) * 5 * 100这个乘了 100 的细节最容易漏漏了就是下单金额差一百倍属于经典翻车点。out_trade_no 用订单号回调时靠它反查订单。notify_url 必须是公网 HTTPS 地址而且域名要有 ICP 备案否则微信支付平台不会往这个地址推送回调。回调验签的代码通常写在独立的 pay_notify.php 里核心是验签和数据一致性?php // 支付结果回调 $xml file_get_contents(php://input); $data json_decode(json_encode(simplexml_load_string($xml, SimpleXMLElement, LIBXML_NOCDATA)), true); if ($data[return_code] ! SUCCESS) { // 微信要求不管成功失败都得回 ACK否则会一直重推 echo xmlreturn_code![CDATA[FAIL]]/return_code/xml; exit; } // 第一步校验签名 $sign $data[sign]; unset($data[sign]); if (makeSign($data, API_KEY) ! $sign) { // 签名不一致回复 FAIL 让微信重新推送 echo xmlreturn_code![CDATA[FAIL]]/return_code/xml; exit; } // 第二步校验金额防篡改 $orderSn $data[out_trade_no]; $row queryOrder($orderSn); if ($row $row[amount] $data[total_fee] $row[status] 0) { // 金额一致且订单未支付更新状态为已支付 $sql UPDATE t_order SET status1, pay_time . time() . WHERE order_sn$orderSn; $db-exec($sql); } // 第三步给微信回 SUCCESS ACK echo xmlreturn_code![CDATA[SUCCESS]]/return_code/xml; exit;参数说明这里必须两步都通过才更新订单状态——签名校验保证消息来自微信支付金额校验保证订单号没有被串改。很多人在回调里只验签不校验金额结果线上被刷单的事常有。还有个细节如果订单早已是已支付状态第二次回调过来要直接返回 SUCCESS不要重复更新否则会把已开始服务的订单状态覆盖掉。4. 部署上线从本地环境到公众号后台4.1 服务器环境准备Nginx PHP MySQL这套源码是传统 PHP 项目不需要 Composer不需要框架解压到 Web 目录就能跑。我一般建议的环境是 CentOS 7 或 Ubuntu 20.04 Nginx 1.18 PHP 7.4 MySQL 5.7。PHP 需要装 fileinfo、openssl、pdo_mysql、curl 这几个扩展用宝塔面板操作的话安装 PHP 时勾选这几个扩展就行。MySQL 里手动建一个库导入源码附带的 .sql 文件再改 config.php 里的数据库连接和公众号参数?php // config.php define(APPID, wx你的appid); // 公众号的 AppID define(APPSECRET, 你的appsecret); // 公众号的 AppSecret define(TOKEN, 自定义token); // 与公众号后台一致 define(MCH_ID, 微信支付商户号); define(API_KEY, 支付API密钥); define(DB_HOST, 127.0.0.1); define(DB_NAME, treesystem); define(DB_USER, tree_user); define(DB_PASS, 数据库密码);这里有个容易被忽略的地方APPID 和 APPSECRET 是公众号的MCH_ID 和 API_KEY 是微信支付商户平台的两者不是一个账号体系很多人第一次配的时候把商户号填到公众号配置里导致 access_token 能拿到但支付永远报商户号不存在。如果遇到《链接内容不属于当前公众号》这种提示多半是公众号主体和网页授权域名对不上这个放到避坑章节细说。Nginx 侧需要配置一个 HTTPS 站点伪静态规则可以按 ThinkPHP 那套写但更简单的方式是让所有请求都走 index.phpserver { listen 443 ssl; server_name treesystem.cn; ssl_certificate /path/ssl.pem; ssl_certificate_key /path/ssl.key; root /www/wwwroot/treesystem; index index.php; location / { try_files $uri $uri/ /index.php?$query_string; } location ~ \.php$ { include fastcgi_params; fastcgi_pass unix:/tmp/php-cgi-74.sock; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; } }这段配置里 try_files 的作用是如果请求的不是真实文件比如异常路径就把请求重写到 index.php由 PHP 侧再做路由判断。fastcgi_pass 指向的 socket 路径要和你安装的 PHP 版本对应宝塔默认是 /tmp/php-cgi-74.sock如果你装的是 8.0路径就得改成 80 结尾。证书有效期一般是一年建议在续期提醒里设个日历提醒不然过期那天整个系统静默挂掉用户看到的只是公众号无响应。4.2 公众号后台配置URL、Token 与消息加解密Nginx 配好后公众号后台的配置路径是「设置与开发」→「基本配置」→「服务器配置」。URL 填 https://你的域名/index.phpToken 填 config.php 里的 TOKEN消息加解密方式选「明文模式」最省事生产环境再改「安全模式」。点提交时微信会发一个 GET 请求验证你的 URL这时候就要确保 index.php 能正常响应如果提示“验证失败”99% 是签名逻辑或者 Token 不一致。配置完成后还要做一件事在公众号后台「IP 白名单」里把服务器的公网 IP 加进去否则调用微信接口时 access_token 会被拒。这一步是源码教程里最容易漏的漏了的典型现象是树洞功能能收发消息但一旦触发获取用户信息或者发送模板消息就报 40164。4.3 公众号迁移与环境切换从测试号到认证服务号很多人先用微信测试号调通源码测试号的好处是接口权限全开不用备案也能配但测试号的 appid 和正式号的完全不一样。切换到正式号时除了改 config.php 里的 APPID、APPSECRET还要注意这几个联动项网页授权域名、JS 接口安全域名、支付授权目录。如果你把源码从一个主体迁移到另一个主体也就是遇到公众号迁移的情况原来的 appid 会作废需要把迁移后的新 appid 全部替换同时微信支付那边也要重新绑定商户号和公众号的关联关系。公众号迁移这个操作很多人不知道会连带影响支付迁移完成后公众号主体变了JSAPI 支付里 appid 和新主体必须一致否则调起支付直接报“当前页面 URL 未注册”。我的习惯是每次环境切换后用一张旧的测试单走一遍全流程确认下单、回调、退款三个环节都能跑通再放量。注意测试号能调通的接口切到正式号不一定全部默认可用尤其是支付相关权限需要单独申请不是迁移完就自动带过去。5. 避坑排查微信公众号开发的高频翻车现场5.1 服务器配置验证失败signature error现象在公众号后台点提交服务器配置微信提示“验证失败”。原因最常见的是 Token 不一致其次是 checkSignature() 里排序方式写错。很多人习惯用 sort($tmpArr) 不传第二个参数微信要求按字符串排序如果 token 或时间戳是纯数字默认排序可能得到不同结果。解决把 define(TOKEN, ...) 和后台填写的值逐字符比一遍然后给 sort 传入 SORT_STRING。改完先在浏览器直接访问 https://你的域名/index.php?signaturexxtimestampxxnoncexxechostrhello 手动验一遍能回显 hello 再回后台提交。5.2 链接内容不属于当前公众号现象用户点击菜单跳转 H5 页面时微信提示“链接内容不属于当前公众号”。原因公众号的“网页授权域名”只支持配置一个域名而你的树洞 H5 页面部署在另一个域名上或者当前访问的 URL 有端口号微信要求域名完全一致端口、路径前缀都不能差。另外域名必须完成 ICP 备案并经过微信校验文件验证。解决把网页授权域名改成 H5 页面对应的主域名比如 h5.treesystem.cn然后下载微信校验文件放到该域名的根目录。发布失败也是同一个套路——如果你用未备案域名做支付跳转页微信会直接拦截用户根本到不了下单页。5.3 支付回调收不到订单一直是待支付现象用户微信里钱已经扣了但后台订单状态还是待支付。原因notify_url 写成了 http 或者域名没备案或者回调地址返回报错导致微信支付重推失败还有一种常见情况是回调代码里没有在 return_code 为 SUCCESS 时立即回 ACK微信认为回调超时。解决先看微信支付商户平台的“交易通知”日志确认通知是否发出以及返回码是什么。代码里必须保证最开头就回应 ACK即便验签失败也要回应 FAIL 而不是不回应。其次确认服务器公网 IP 能访问 https://你的域名/pay_notify.php在回调脚本开头写一行 file_put_contents 记录原始报文是最快的排错方式。5.4 中文命令前缀截断树洞发布乱码现象用户发 “#树洞 今天好累”结果系统提示“多说一点嘛至少 2 个字”。原因PHP 的 substr() 按字节截取“#树洞”在 UTF-8 下是 1 3 3 7 个字节代码里如果写 substr($content, 5) 就把“树”字截成了半个后续判断长度自然失败。解决所有中文字符串操作统一用 mb_substr($content, $offset, null, utf-8)并且在文件头部声明 mb_internal_encoding(utf-8)。这个坑在 3.1 里已经说过它就是典型的“本地不回显、线上必炸”的玄学问题本地用英文内容测不出问题一上中文必现。5.5 获取不到用户 openid一直停在下单页现象点下单按钮JSAPI 调起支付时报“缺少参数 openid”。原因网页授权没走通。snsapi_base 静默授权返回 openid 的前提是当前页面 URL 的域名在“网页授权域名”白名单里而且授权回调地址 code 是一次性的用两次就会报错。另一个坑是本地调试用内网 IP微信回调根本无法到达。解决检查授权回调地址 redirect_uri 是否做了 urlencode确认链接不带端口。后端拿到 code 后请求 https://api.weixin.qq.com/sns/oauth2/access_token 换取 openid这个接口返回的 openid 和消息回调里的 FromUserName 是同一个但和当前用户的 unionid 不一定相同不要混用。6. 进阶内容安全检测与全流程验证6.1 接入微信内容安全接口树洞是 UGC 场景用户说什么你都拦不住但微信对公众号的 UGC 内容管控很严涉政、涉黄、广告引流一旦被举报轻则接口封禁重则公众号封号。我拿到任何源码都会先给发布内容加一道微信内容安全检测用的就是微信官方 msg_sec_check 接口?php // 发布前先过内容安全检测 function checkContent($content, $openid) { $accessToken getAccessToken(); // 缓存的 access_token $url https://api.weixin.qq.com/wxa/msg_sec_check?access_token . $accessToken; // 新版接口用 version2检测更准 $response httpPost($url, json_encode([ content $content, version 2, openid $openid ], JSON_UNESCAPED_UNICODE)); $result json_decode($response, true); // errcode 为 0 表示通过87014 表示内容违规 return ($result[errcode] ?? -1) 0; }这个接口免费但需要提前获取并缓存 access_token。我的做法是写一个内存缓存函数access_token 有效期 7200 秒每次取的时候先查缓存过期再请求避免频繁调用触达频率限制。content 参数直接传用户原文不要做任何转义接口内部会做分词和命中规则。发布树洞帖子前先过这道检测违规内容直接丢弃并提示用户。6.2 用日志和完整链路验证系统健康度支付订单还有一个兜底逻辑必须做待支付订单如果用户一直不付会在数据库里堆积。我习惯在订单表加一个 expires_time 字段下单时写入当前时间加 30 分钟用一个定时任务每分钟扫一次把超过 expire 且 status0 的订单置为已关闭状态这比到了月底看到几百条脏数据再手工清理强得多。验证系统是否稳定我有一个固定动作每周手动下一笔 1 分钟陪聊订单从下单、支付、回调、服务结束、退款五个环节各走一遍记录每一步耗时。这套体系的健康度就看订单状态流转有没有卡点卡在哪里日志里一定有线索。树洞源码最怕的就是业务流程黑匣子化所以我常态会在入口文件第一行记一条日志把每次收到微信消息的 MsgType、FromUserName、时间戳写进文件排查问题时直接 grep 这个文件比在浏览器里猜快得多。从这个项目之后我就养成了习惯每次改配置、改环境先强制自己走一遍“发布 → 捞取 → 下单 → 支付 → 回调”的完整链路跑不通就回看日志直到全流程零手动介入通过再上线。这套陪聊源码本身的逻辑不复杂复杂的是微信生态的各种审核和外链限制希望这篇拆解能帮你在部署时少踩几个坑。本文还有配套的精品资源点击获取

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

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

免费获取报价 →
↑