共享WiFi小程序源码解析:从扫码连接到广告分成
简介共享WiFi营销小程序源码是一套面向线下门店、代理商和平台运营者的综合商业系统围绕扫码连接门店无线网络、广告分成、用户沉淀来设计整合了门店WiFi、热点资讯、代理推广、广告营收、红包营销、社区拼团与自媒体发布等模块。压缩包共2000个文件以PHP后端逻辑、HTML页面和JavaScript交互为主并包含CSS样式、PNG/GIF/SVG图片素材、JSON配置及小程序WXML/WXSS文件整体约25.16MB目录结构完整便于部署和二次开发。目前已有1180人学习下载。通过阅读源码可以快速理解共享WiFi平台从扫码认证、广告展示、代理商分润到社区拼团、内容发布的全链路实现思路适合具备小程序或PHP开发基础的开发者直接参考也可作为私有化部署或功能扩展的起点省去从零搭建的重复工作。1. 共享WiFi的本质是什么源码解决的是哪一段共享WiFi不是一个新概念但真正跑通的团队很少。原因不在扫码连WiFi这个动作本身而在于流量转化率——用户扫完码、连上网、关掉小程序整个过程不到十秒广告根本没机会曝光。这套即用WIFI营销源码的立身之本是把“连接WiFi”这个刚需动作做成了流量入口用户想上网必须先经过广告页、红包页或资讯页门店和代理商按曝光和点击拿分成。换句话说它卖的不是网络是“用户停留时长”。从技术上拆这套源码涉及小程序前端、商家后台、代理商分销、广告分成计算、红包结算和社区拼团等多个模块。适合谁一类是本地生活服务商想用最低成本把周边门店的WiFi流量收编成自己的私域另一类是独立开发者需要一套能直接改的现成业务骨架而不是从零搭用户、订单、分账这套基础设施。接下来的内容会按“入口交互 → 商业链路 → 数据设计 → 上线排错”的顺序把源码里最核心的几段逻辑讲透。2. 扫码进场与连接态保持从二维码到 WiFi 的交互设计2.1 扫一扫拉起小程序URL Scheme 与 scene 参数即用WIFI的线下码不是简单的静态码而是带参数的渠道码。每一张贴在门店的二维码都绑定了一个门店ID和桌台位。用户用微信扫这个码会直接拉起小程序并进入连接页整个过程用户无感。实现上用的是微信的URL Link配合scene参数小程序端通过onLoad里的options.scene拿到参数。注意scene的值需要decodeURIComponent解码一次。// pages/connect/index.js Page({ onLoad(options) { let scene decodeURIComponent(options.scene || ); // scene 形如: m1008tA12 const params this.parseScene(scene); this.setData({ merchantId: params.m, tableNo: params.t }); this.initWifi(); }, parseScene(scene) { const result {}; scene.split().forEach(kv { const [k, v] kv.split(); result[k] v; }); return result; } });这段代码的关键不是解析本身而是参数校验。门店ID直接决定小程序调哪个接口拿WiFi密码如果scene可以被篡改就存在越权拿密码的风险。实际工程里scene内容建议用签名串代替明文m1008tA12signmd5(mtsalt)后端验签通过才下发密码。另外二维码的生成侧常见做法是用PHP在后端调微信接口换取URL Link再配合二维码生成库输出成图片方便门店下载打印。2.2 connectWifi 的边界iOS 引导页与 Android 权限差异拿到WiFi账号密码之后小程序端调用wx.connectWifi完成连接。这里有个很多初做共享WiFi的开发者容易踩的坑iOS上小程序无法直接连接WiFi微信只允许跳转到系统设置页让用户手动连接Android下虽然支持直接连接但Android 10及以上版本对WiFi列表的读取权限收得非常紧wx.getWifiList在部分机型上会返回空数组。connectWifi() { wx.startWifi({ success: () { wx.connectWifi({ SSID: this.data.ssid, password: this.data.password, success: () { wx.showToast({ title: 连接成功, icon: success }); }, fail: (err) { if (err.errCode 12000) { wx.showModal({ title: 需要手动连接, content: iOS系统请前往设置页手动连接WiFi, confirmText: 去设置, success: () wx.openSetting() }); } } }); } }); }判断errCode 12000是系统错误在iOS上几乎都会走到这里。所以代码里要做好降级引导否则用户会卡在失败提示上直接流失。Android端还要注意一个细节wx.connectWifi只支持连接无密码的开放网络或者WPA/WPA2加密网络不支持企业级802.1X认证。门店路由器的加密方式必须在后台生成密码时就校验一遍否则到用户手机上才报错排查成本很高。2.3 连接态保持为什么 SessionKey 不能丢即用WIFI的商业模式里用户连上WiFi后要“停留”在小程序里完成广告浏览或红包领取才能真正产生收益。这就带来一个技术问题WiFi连接成功后小程序切后台再回来前端拿到的WiFi状态还在吗实际上wx.getConnectedWifi在Android上返回的是当前连接的WiFi信息iOS则不一定能拿到。所以源码里不能依赖系统API判断用户是否还在门店而是用服务端会话保持——用户扫码进入时生成一个带有效期的token每次广告点击、红包领取都带着token走。这里的SessionKey不是指微信登录的session_key而是业务侧自己签发的短期凭证。常见做法是Redis里存wifi_session:{token} - {merchant_id, user_id, expire}过期时间设计成跟广告页停留时长匹配比如5分钟。过期后用户再点广告接口返回401前端再引导重新扫码。这个设计的目的很直接把“连接WiFi”和“浏览广告”绑定在一起防止用户连上就跑门店收益没法结算。3. 广告分成与红包结算链路一次点击背后的分账处理3.1 曝光埋点与点击埋点必须分离广告主结算看曝光门店提成看点击代理商看流水三者口径不一样埋点如果混在一起月底对账必然打架。即用WIFI源码里把广告事件分成ad_show和ad_click两类分别落到不同的日志表。曝光是在广告页渲染完成时上报点击是用户真实点了跳转链接才上报。网络抖动导致的重复上报要靠前端幂等键去重这个幂等键用{user_id}_{ad_id}_{scene}_拼接MD5生成同一个用户同一广告同一场景只记一次。-- 广告事件流水表 CREATE TABLE ad_event_log ( id bigint(20) NOT NULL AUTO_INCREMENT, event_type varchar(10) NOT NULL COMMENT show/click, user_id int(11) NOT NULL, ad_id int(11) NOT NULL, merchant_id int(11) NOT NULL, scene varchar(32) DEFAULT COMMENT 入口场景, event_id varchar(64) NOT NULL COMMENT 幂等键, client_ip varchar(64) DEFAULT , create_time int(11) NOT NULL, PRIMARY KEY (id), UNIQUE KEY uk_event_id (event_id), KEY idx_merchant_time (merchant_id,create_time) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;这张表是后续所有分账和风控的数据源。event_id的唯一索引直接从数据库层面挡住重复上报业务代码不需要加分布式锁。create_time用int类型存Unix时间戳方便做小时级别的聚合报表。如果量级上来可以考虑按天分表但早期单表加索引足够扛住单个城市几千家门店的并发。3.2 分佣规则引擎广告位溢价与门店分成比例分佣逻辑是这套系统的核心也是源码里最值得细读的部分。一段广告的收益不是一个固定值而是由多个因素动态计算出来的广告主出价、广告位类型开屏/插屏/Banner、用户所在城市等级、门店的行业分类。源码里用一个分佣规则表来配置这些权重而不是写死在代码里这样才能在运营层面快速调整。// 分佣计算逻辑伪代码 public function calcCommission($adEvent) { $rule $this-getRule($adEvent[ad_id], $adEvent[merchant_id]); // 基础收益 广告主出价 × 广告位系数 × 城市系数 $baseIncome $rule[bid_price] * 100 * $rule[slot_factor] * $rule[city_factor]; // 平台抽成30%剩余部分门店和代理商按比例分配 $platformShare round($baseIncome * 0.30, 2); $distributable $baseIncome - $platformShare; $merchantShare round($distributable * $rule[merchant_rate], 2); $agentShare $distributable - $merchantShare; return [ raw $baseIncome, platform $platformShare, merchant $merchantShare, agent $agentShare, ]; }这里的bid_price是广告主出价单位是分slot_factor按广告位的实际填充率设置比如开屏1.2、插屏1.0、Banner 0.6city_factor根据一二三线城市设置阶梯。门店分成比例merchant_rate不是固定的新门店引导期可以给到60%-70%稳定后调回50%。这些参数都建议放到后台可配置因为运营策略会频繁变化如果每次调整都发版迭代速度完全跟不上。分佣计算的结果必须落到一张独立的账单流水表并在当天结算窗口跑定时任务做汇总不能图省事在用户点击的同步逻辑里直接改余额。3.3 红包账户与提现审核资金安全的风控红线红包资金走的是微信商户代付这里面有一个合规红线用户余额必须与平台自有资金分离。即用WIFI源码里的处理方式是建一张user_wallet账户表收入明细记到wallet_ledger提现时再联动冻结和扣减。用户看到的“红包”本质上不是现金入账而是浏览广告后发放的“浏览收益”余额满1元后才能申请提现平台在后台人工审核后再调用微信商家转账接口打款。CREATE TABLE user_wallet ( user_id int(11) NOT NULL, balance int(11) NOT NULL DEFAULT 0 COMMENT 可用余额单位分, frozen int(11) NOT NULL DEFAULT 0 COMMENT 冻结余额, total_income int(11) NOT NULL DEFAULT 0 COMMENT 累计收入, withdraw_total int(11) NOT NULL DEFAULT 0 COMMENT 累计提现, updated_at int(11) NOT NULL, PRIMARY KEY (user_id) ) ENGINEInnoDB;资金流转这块不是所有余额都能提现。很多共享WiFi平台的分佣做的是“门店充值购买WiFi时长用户扫完码门店获得积分积分可提现”这个闭环。源码里没有走真实资金池而是用积分体系做内部记账提现时再向商家号发起转账。这里要特别检查的一点是门店申请提现时系统要校验这张门店绑定的商户号和小程序主体是否一致避免门店经营者恶意利用他人商户号代收资金。风控维度至少覆盖设备指纹、IP频次、提现时段三个层面。风控维度规则建议触发后的动作同设备扫码频次同一设备一天最多扫码12次触发滑块验证码同IP点击频次同IP一小时最多点击20次该IP暂停计费2小时新用户红包阈值注册当天红包上限5元超出部分转为积分,不可提现提现频次同一门店一天最多提现2次超过自动进入人工审核4. 核心表结构与门店/代理商API设计数据是怎么被组织起来的4.1 实体关系从用户到门店的链路设计这套系统的实体比普通电商要复杂一层因为同样的一个用户在不同角色身上有不同的身份。用户扫门店码是C端消费者同时他可能是另一个门店的代理商。所以源码里的表结构没有把用户角色写死在某个字段里而是用角色关联表动态绑定——user,store,agent,agent_store_relation四张表完成最基本的闭环。store表里最关键的两个字段是ssid和wifi_password。共享WiFi的密码更新频率很高源码里设计了一个password_update_at时间戳运营后台可以配置密码轮换周期。常见做法是每周自动换一次密码同时通过短信或小程序订阅消息通知门店老板。注意这里有个反直觉的经验密码轮换太频繁每天一次会导致老用户流失因为常客记不住密码也不愿意每次看广告重新扫码太慢一个月以上又会让已经连上WiFi的老用户失去再次打开小程序的动机。两周到一个月是比较合理的区间。CREATE TABLE store ( id int(11) NOT NULL AUTO_INCREMENT, merchant_id int(11) NOT NULL COMMENT 所属商户/品牌, agent_id int(11) DEFAULT NULL COMMENT 归属代理商, store_name varchar(64) NOT NULL, ssid varchar(64) NOT NULL COMMENT WiFi名称, wifi_password varchar(128) NOT NULL COMMENT 当前密码, password_update_at int(11) NOT NULL, province varchar(32) DEFAULT , city varchar(32) DEFAULT , district varchar(32) DEFAULT , address varchar(255) DEFAULT , status tinyint(4) NOT NULL DEFAULT 1 COMMENT 1正常 0禁用, PRIMARY KEY (id), KEY idx_agent_city (agent_id,city) ) ENGINEInnoDB;4.2 代理商管理独立后台还是嵌套在小程序里这套源码的代理商不是普通用户他们有独立的结算关系和店铺管理权限。在实际部署中代理商后台通常是单独一套Web管理系统而不是放在微信小程序里——代理商需要批量导入门店数据、查看每日收益日报、申请提现这些操作在小程序上做既费劲又不专业。代理商与门店的绑定关系通过agent_store_relation表维护代理商可以看到自己名下门店的广告曝光量、点击量、预估收益和实际结算四个维度。// 代理商日报聚合查询 public function agentDailyReport($agentId, $date) { $sql SELECT DATE_FORMAT(e.create_time, %Y-%m-%d) AS day, COUNT(DISTINCT e.merchant_id) AS active_stores, SUM(CASE WHEN e.event_typeshow THEN 1 ELSE 0 END) AS total_shows, SUM(CASE WHEN e.event_typeclick THEN 1 ELSE 0 END) AS total_clicks, ROUND(SUM(CASE WHEN e.event_typeclick THEN c.agent_income ELSE 0 END) / 100, 2) AS agent_income FROM ad_event_log e LEFT JOIN commission_log c ON c.event_id e.event_id WHERE e.agent_id ? AND e.create_time BETWEEN ? AND ? GROUP BY day; // 参数绑定执行... }注意这里的SQL有一个性能陷阱。ad_event_log会快速增长如果代理商数量上来这个聚合查询会越跑越慢。常见做法是建一张agent_daily_report汇总表每天凌晨跑定时任务把昨天的数据按代理商维度聚合好白天报表页面只查汇总表。这个优化在做代理商后台时就要考虑进去而不是等数据量大了再改架构。4.3 API接口约定密码下发必须走服务端小程序端拿到WiFi密码的接口有三个限定条件接口必须校验merchant_id和scene的归属关系、接口返回的密码要做脱敏处理只显示后四位、接口必须有访问频率控制防止被爬虫批量抓取密码。源码里给的是标准的REST风格接口设计按商家的经验这个方法整体可行但需要补一个缓存层——同一个门店一小时内重复扫码的用户直接读Redis缓存不用每次都查MySQL。// 小程序端调用密码下发接口 wx.request({ url: https://api.example.com/wifi/password, method: POST, data: { merchant_id: this.data.merchantId, scene: this.data.scene, token: this.data.sessionToken }, success(res) { if (res.data.code 0) { // 返回的密码字段只包含后四位明文用于前端展示 this.setData({ ssid: res.data.ssid, password: res.data.password, // 实际完整密码 passwordMask: res.data.password_mask // 仅展示后四位 }); this.connectWifi(); } else { wx.showToast({ title: res.data.msg, icon: none }); } } });接口这里的风险点不在接口本身而在前端拿到密码之后。如果用户把密码分享出去门店的收益就会直接受损。所以源码里还有一层设计——密码与用户绑定接口返回的密码在当前门店是有效的但拿到其他门店去用会直接失效。这要求门店WiFi密码在服务端按门店维度做映射校验连接成功后前端上报连接结果服务端才能确认这次分发是有效的。5. 发布验证与真机排错类目审核、备案与WiFi连接失败5.1 微信小程序备案与类目选择的实操建议小程序备案是上线前绕不开的一步。以现在的备案流程在小程序后台“设置-基本设置-备案信息”里填写需要注意“备注信息”这里不要空着不要写“无”。正确写法是写清楚小程序提供的具体服务例如“本小程序用于为用户提供门店WiFi连接服务并展示门店信息及优惠活动”字数和内容要对应实际功能。共享WiFi类小程序最容易驳回的地方是类目选择如果选了“社交”会被要求提供《非经营性互联网信息服务备案核准》所以建议优先考虑“生活服务 生活缴费”或“工具 信息查询”这两个类目。选错类目不仅驳回还可能导致流量主功能无法开通而广告收入是这套系统的命脉流量主开通条件是有累计1000名独立访客这个在早期要主动引导门店做拉新活动凑够门槛。5.2 connectWiFi 错误码对照与处理策略真机调试时wx.connectWifi的报错信息经常比文档更含糊以下是排查过程中最常见的几类错误码及应对方式错误码含义处理策略12000系统错误iOS降级引导手动连接;Android重试一次后刷新WiFi列表12002网络未连接引导用户先开启WiFi开关,再执行连接12004SSID无效检查门店后台录入的SSID是否包含特殊字符,建议统一改成纯英文12006密码错误调用后台校验接口确认是当前生效密码,而不是轮换前的旧密码12004在中文SSID场景下特别高频微信的WiFi接口对中文SSID支持不稳定源码里的门店录入表单应该预先过滤掉非ASCII字符否则上线后这类门店会持续产生客诉。另外Android 12以上机型连接WiFi时如果同时开启了随机MAC地址可能导致门店路由器的MAC白名单失效这个目前没有代码层的最优解只能在门店运营指引里标注建议关闭随机MAC。5.3 动态密码轮换的定时任务设计共享WiFi平台的日常维护工作中动态密码轮换是最容易出问题的环节。源码里提供了定时任务脚本每天凌晨扫描password_update_at超过轮换周期的门店生成新密码并推送通知给门店老板。这个小功能的坑在于如果定时任务只负责改数据库里的密码而路由器里的密码没有同步变更用户扫码拿到的密码会全部失效。所以轮换任务必须分两步走——先调用路由器管理接口改密码成功后再更新数据库两步都成功才推送通知。家用路由器基本不提供开放API实际操作中通常是引导门店店员手动改路由并点击“确认已修改”按钮系统检测到确认动作后才更新数据库。这个“人工确认”的设计很土但比自动同步可靠得多尤其是在对接杂牌路由器的时候。本文还有配套的精品资源点击获取