简介这是一套面向移动端微信积分兑换商城的H5项目源码定位于帮助开发者快速搭建具备签到、游戏积分、代金券兑换等功能的积分商城页面。基于HTML5技术实现适配手机微信环境适用于电商运营、会员营销等场景也适合前端学习者参考完整积分商城的前端实现。压缩包含112个文件体积约1.19MB其中20个html构成商城核心页面9个js负责交互逻辑3个css统一样式另有80张png/jpg图片素材支撑界面视觉。目前已有623人浏览学习。资源内提供完整的积分商城网站模板涵盖首页、签到、兑换、个人中心等多个页面的源码开发者可直接运行预览并在此基础上调整积分上限、兑换规则和页面文案也可结合微信支付、分享等接口做深度集成。对希望低成本搭建积分体系或研究H5商城交互细节的读者这套源码具有直接的参考与复用价值。1. 手机微信积分兑换商城 H5先拆开这套 zip 再决定要不要接手运营同事丢过来一个「手机微信积分兑换商城H5.zip」说这是现成的积分商城解压就能用。做过微信生态的都清楚这种压缩包最像黑匣子首页 Demo 看着完整等你配微信网页授权、配 JS-SDK 分享、接真库存的时候每一步都可能卡住。这套 H5 积分商城的本质不复杂前端是跑在微信浏览器里的移动商城页面后端负责微信授权拿 openid、积分扣减、库存锁和订单生成。它解决的是「公众号或服务号用户手里有积分却缺一个能直接兑换商品的落地页」这个具体问题。适合谁手里有微信流量入口、又不想从零写商城后台的团队。接手前先确认三件事授权链路是否闭环、积分是否走流水表、下单库存有没有并发控制这三条不过关后面全是血泪经验。2. 跑起来之前先看懂三件事微信授权、积分账务与商城表结构拆开包先别急着配域名先看数据层和授权链路。这一章是后面所有操作的地基地基没打正第 3 章的代码抄得再对也跑不通。很多团队拿到现成 H5 商城第一件事就是传服务器结果授权回调失败、积分对不上账回头再改表结构比重新写还难受。2.1 微信网页授权用 openid 做用户主键别自己造账号体系微信内 H5 商城要做的第一件事是识别用户。小程序用 code 换 openidH5 走的则是微信网页授权 OAuth2.0。常见做法是用户进入页面时先跳一次授权链接微信回调带回 code后端再用 code 换 access_token 和 openid。这里有个容易误判的点商城场景下绝大多数积分系统都用 snsapi_base 静默授权因为它只花一次跳转、用户无感知只有你需要头像昵称做展示时才改用 snsapi_userinfo而且用户必须手动点同意。我一般建议积分商城默认用 snsapi_base头像昵称等用户兑换时再补换来的是更低的授权流失率。下面这段是后端处理微信授权回调的常见写法用 PHP 伪代码表示// wechat_auth.php 授权回调处理 $code $_GET[code] ?? ; if (!$code) { // 拼授权链接appid 换成你的服务号 appid $redirect urlencode(https://你的域名/index.php/api/wechat/auth); $url https://open.weixin.qq.com/connect/oauth2/authorize? . appidAPPIDredirect_uri{$redirect} . response_typecodescopesnsapi_basestate123#wechat_redirect; header(Location: $url); exit; } // 用 code 换 openidappid 与 secret 从配置文件读取 $tokenUrl https://api.weixin.qq.com/sns/oauth2/access_token? . appidAPPIDsecretSECRETcode{$code}grant_typeauthorization_code; $resp json_decode(file_get_contents($tokenUrl), true); $openid $resp[openid] ?? ; if (!$openid) { // 常见失败code 被重复使用、secret 错误、用户拒绝授权 exit(授权失败请从商城入口重新进入); } // 用 openid 查本地用户表不存在则自动注册 $user queryUserByOpenid($openid); if (!$user) { $user createUserByOpenid($openid); } // 写入 session后续接口靠 session 识别身份 session_start(); $_SESSION[openid] $openid;这段代码的关键在最后三步查用户、建用户、写 session。很多人图省事每次请求都拿 code 换一次 openid这既浪费接口配额又会撞上 code 只能使用一次的限制——微信的 code 有效期五分钟且只认一次重复拿去换 token 必然失败。参数上注意 scope 必须写 snsapi_basestate 随意但建议带随机值防 CSRF这样回调回来能校验是不是自己发起的请求。2.2 积分账务模型流水表和冻结额度决定你后面对账累不累多数积分商城翻车不是翻在前端页面而是翻在账务模型。如果代码里直接 update users set points points - 100那就等于把账户余额当计数器用兑换失败、退货、后台手动调账全都没法追溯。我见过的靠谱实现都至少有一张积分流水表和一张积分冻结表流水表只增不改每一笔积分变动都插入一行人工调账也走流水负分允许出现但必须带业务单号。流水表的另一层作用是给运营留后悔药用户投诉「积分少了」时你只需要按 openid 查流水就能知道他每一分去了哪而不是对着一个余额数字干瞪眼。下面是一张典型的设计CREATE TABLE points_ledger ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, user_openid VARCHAR(64) NOT NULL COMMENT 微信openid, change_type TINYINT NOT NULL COMMENT 1签到 2兑换扣减 3兑换退回 4后台调整 5过期扣除, points INT NOT NULL COMMENT 变动积分扣减为负数, order_no VARCHAR(32) DEFAULT COMMENT 关联订单号调账可空, remark VARCHAR(255) DEFAULT COMMENT 备注, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, KEY idx_user (user_openid, created_at), KEY idx_order (order_no) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE points_freeze ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, user_openid VARCHAR(64) NOT NULL, order_no VARCHAR(32) NOT NULL, points INT NOT NULL COMMENT 冻结的积分数, status TINYINT NOT NULL DEFAULT 0 COMMENT 0冻结中 1已扣减 2已退回, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;冻结表解决的是「用户下单后、订单完成前这笔积分到底算谁的」这个模糊地带。下单先冻结订单完成才正式扣减订单取消则解冻退回。这样设计的好处很明显用户一次性下了五单五笔冻结都在最后一单失败时不会把已经扣掉的分再算一遍。参数上注意 change_type 必须用注释写清楚否则三个月后你自己看着 0、1、2 也想不起来是什么含义。2.3 商城表结构商品、库存、兑换记录怎么设计最省事商品和订单表相对直白但有几个字段最好一开始就加上省得后面加需求时改表。商品表需要区分「积分价」和「现金价」纯积分商品现金价为 0如果做积分加现金混合支付则需要同时记录两个值。很多二手代码只留一个 price 字段活动搞积分抵现时才发现算不出应付金额只能硬编码这是最常见的返工点。CREATE TABLE product ( id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, title VARCHAR(100) NOT NULL, cover_image VARCHAR(255) NOT NULL, points_price INT UNSIGNED NOT NULL DEFAULT 0 COMMENT 积分价0表示不支持积分购买, cash_price DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT 现金价0表示纯积分, stock INT NOT NULL DEFAULT 0 COMMENT 库存下单扣减, status TINYINT NOT NULL DEFAULT 1 COMMENT 1上架 0下架, sort INT NOT NULL DEFAULT 0, KEY idx_status (status, sort) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE exchange_order ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, order_no VARCHAR(32) NOT NULL UNIQUE COMMENT 业务订单号, user_openid VARCHAR(64) NOT NULL, product_id INT NOT NULL, points_cost INT NOT NULL COMMENT 实际扣减积分, cash_cost DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT 实际支付现金, status TINYINT NOT NULL DEFAULT 0 COMMENT 0待确认 1已完成 2已取消 3已退款, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, KEY idx_user (user_openid, status) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;为什么积分价和现金价分开存因为积分兑换商城经常做「积分抵现」活动原价 100 元支持 500 积分抵 20 元。两个值分字段存后续算应付金额就是一行公式塞在一个字段里改活动规则时只能写死逻辑。另外订单号建议用后端生成的长单号日期加随机数不要用数据库自增 id 直接当单号给用户原因在订单退款对账时你就会明白——自增 id 暴露订单量而且不好做全局唯一。3. 把 H5 商城本地跑起来从解压到完成一次积分兑换数据层看懂之后开始动手。这一章按「解压、配环境、授权联调、做成一单」的顺序走每一步都能直接照着做。如果解压后文件都认不全先做 3.1 的目录识别别把后端模板页面当静态文件传到 CDN 上那是新手最容易犯的错。3.1 先读配置文件前后端分离还是模板渲染决定本地怎么起解压后第一件事不是双击 index.html而是看目录结构。常见做法分两种一种是 PHP 模板渲染页面、接口、数据库都在一个项目里另一种是前后端分离H5 前端是纯静态资源后端只出 JSON 接口。识别方法很简单看根目录有没有 index.php 或 application 目录有就是模板渲染只有 static、assets 和 config.js 之类就是前后端分离。前者本地要装全套运行环境后者可以先把静态页随便放到静态服务器上看效果。两种形态的配置文件分别写在 .env 或 config.php 里内容大致是这些# .env 环境配置示例 APP_NAMEpoints-mall APP_ENVlocal APP_DEBUGtrue # 微信服务号配置 WX_APPIDwx1234567890abcdef WX_SECRETabcdef1234567890abcdef1234567890 # 数据库连接 DB_HOST127.0.0.1 DB_PORT3306 DB_NAMEpoints_mall DB_USERroot DB_PASSroot # 站点域名微信授权回调会用到必须与微信后台一致 APP_URLhttps://mp.example.com这里最容易踩的坑是 APP_URL。微信授权回调要求域名和微信公众平台后台配置的「网页授权域名」完全一致连端口都要一致而且域名必须是公网可访问的。本地开发如果直接写 localhost授权必然失败——微信服务器根本访问不到你的电脑这一步卡住的人最多。3.2 微信授权回调的本地联调没有公网域名也能测本地起服务以后授权回调卡在「微信服务器要回调你的公网地址」这一步。常见做法有两种一是准备一台测试服务器把代码部署到测试服域名上二是本地起一个内网穿透服务把 80 端口映射到一个临时公网域名再把微信后台的网页授权域名临时改成这个临时域名。第二种适合快速验证但临时域名每次重启都会变改一次微信后台至少等几分钟生效别在授权流程里反复切。映射起来之后数据库里的 APP_URL 要同步改成临时域名不然回调地址拼接出来还是 localhost。整个链路是用户微信打开页面302 跳微信授权微信带 code 回调你的临时域名后端换 openid种 session跳回商城首页。调试时重点看浏览器 Network 面板里 /connect/oauth2/authorize 的请求和回调请求code 有没有带到、后端有没有报错一目了然。如果回调请求根本没发出去优先检查微信后台域名配置而不是代码。提示授权回调调试最好用微信开发者工具它内置了本地代理能力可以自定义域名映射比真机验证快得多。真机只留到最后验证分享和支付。3.3 最小兑换闭环商品详情 → 提交兑换 → 扣积分 → 生成订单配置通了就该验证核心链路。最小闭环是前端展示商品详情 → 用户点立即兑换 → 后端校验积分和库存 → 扣积分、扣库存、生成订单 → 返回结果。下面这段是把兑换逻辑写成单事务的示范重点看执行顺序// exchange.php 兑换接口核心逻辑 public function doExchange($productId, $openid) { // 开启事务防止并发下库存和积分不一致 $pdo-beginTransaction(); try { // 1. 锁定商品行防止库存超卖 // 注意必须用 FOR UPDATE普通 SELECT 在高并发下会读到同一份库存 $stmt $pdo-prepare( SELECT id, title, points_price, stock FROM product WHERE id ? AND status 1 FOR UPDATE ); $stmt-execute([$productId]); $product $stmt-fetch(); if (!$product) throw new Exception(商品不存在或已下架); if ($product[stock] 0) throw new Exception(库存不足); if ($product[points_price] 0) throw new Exception(该商品不支持积分兑换); // 2. 读取用户当前可用积分余额总积分-冻结中 $userPoints getAvailablePoints($pdo, $openid); if ($userPoints $product[points_price]) { throw new Exception(积分不足当前可用 . $userPoints); } // 3. 扣库存、写流水、冻结积分、生成订单顺序不能乱 $pdo-prepare(UPDATE product SET stock stock - 1 WHERE id ?) -execute([$productId]); $orderNo date(YmdHis) . mt_rand(1000, 9999); $pdo-prepare( INSERT INTO exchange_order (order_no, user_openid, product_id, points_cost, status) VALUES (?, ?, ?, ?, 0) )-execute([$orderNo, $openid, $productId, $product[points_price]]); $pdo-prepare( INSERT INTO points_ledger (user_openid, change_type, points, order_no, remark) VALUES (?, -2, ?, ?, 兑换商品) )-execute([$openid, $product[points_price], $orderNo]); $pdo-prepare( INSERT INTO points_freeze (user_openid, order_no, points, status) VALUES (?, ?, ?, 0) )-execute([$openid, $orderNo, $product[points_price]]); // 4. 全部成功才提交事务 $pdo-commit(); echo json_encode([code 0, msg 兑换成功, order_no $orderNo]); } catch (Exception $e) { $pdo-rollBack(); echo json_encode([code 1, msg $e-getMessage()]); } }逻辑说明第一步的 SELECT ... FOR UPDATE 是整个防超卖的核心它把商品行锁住第二个用户进来必须等第一个事务提交或回滚库存变负数的情况从根上被堵住。第 2 步的可用积分计算用的是「总积分减冻结中积分」这样同一个用户并发兑换多件商品也不会互相透支。参数说明里最值得注意的是流水表和冻结表的同步写入这里的 change_type-2 表示扣减但在订单点击确认收货之前这 100 积分只是被冻结并没有真正从用户余额里减去。你要是现在不理解这句话回 2.2 再看一眼冻结表的说明。4. 微信积分商城避坑指南授权、库存、支付三大翻车现场到这里代码能跑了但能跑和能上线中间隔着大量真实环境问题。这一章把最常见的四个坑摊开每个都按「现象 → 原因 → 解决」讲清楚全是上线现场真实发生过的问题。4.1 授权回调报 redirect_uri 参数错误微信后台域名没加或加了不同端口现象用户从公众号菜单点进商城微信弹出一个「redirect_uri 参数错误」的空白错误页后端看不到任何业务日志排查半天不知道从哪下手。原因90% 是微信公众平台后台的「网页授权域名」没配或者配的域名与代码里的回调地址不一致。这里有个冷门坑后台配的是 mp.example.com代码里拼接回调地址时用了 https://mp.example.com/api/wechat/auth粗看一致实际上微信在回调时会严格比对协议、域名和端口https 和 http 是两个东西带不带 80/443 端口也是两个东西配了 www 和不配 www 又是两个东西。你以为的一致微信不认。解决先到微信公众平台后台确认网页授权域名再检查代码里 APP_URL 是否与之一致尤其注意协议和端口。我一般会写一条启动日志把当前应用实际拼接的回调 URL 打印出来上线前三分钟盯一眼能避免绝大多数「换个环境就授权失败」的乌龙。4.2 积分扣了但库存是负数上线第一天就被薅出超卖现象运营反馈后台商品库存显示 -3但兑换订单只有 5 单页面也没提示库存不足商品却被换超了。原因下单逻辑没加锁。常见错误写法是先 SELECT 查库存发现大于 0再 UPDATE 扣库存高并发下两个请求同时查到库存1各自往下走库存就变负数了。更要命的是订单状态机不完整用户取消订单只改订单状态、没把库存加回去库存会越差越多最后运营以为系统有 bug实际是逻辑漏洞。解决库存扣减必须写成原子操作把「判断库存大于 0 并扣减」合并成一条 SQLUPDATE product SET stock stock - 1 WHERE id ? AND stock 0然后检查受影响行数为 0 就说明没抢到。如果用了 3.3 的 FOR UPDATE 事务方案同样能达到目的但要注意事务里锁的顺序必须全局一致否则高并发下可能死锁。另外取消订单时把回补库存和积分退回归到同一个事务里别拆成两个接口。4.3 JS-SDK 签名失败分享朋友圈没有缩略图接口报 invalid signature现象页面功能全部正常唯独自定义「分享给好友、分享到朋友圈」的标题和缩略图不生效后台日志里 wx.config 报 invalid signature这个问题看起来很玄学因为同样的代码在安卓上偶尔好、在 iOS 上必挂。原因H5 商城要自定义分享样式前端必须先调 wx.config 注入 JS-SDK 配置而后端要用「当前页面的完整 URL」去生成签名。经典翻车点有两个一是拿配置的域名去生成签名而不是拿用户浏览器地址栏里的完整 URL包括问号参数和井号后面的部分二是页面里有异步路由跳转导致签名时用的 URL 和实际分享时页面 URL 不一致iOS 对 URL 的比对比安卓严格得多。解决签名 URL 必须用前端location.href.split(#)[0]动态获取再传给后端参与 SHA1 签名。更保险的做法是单页应用里每次路由变化后重新调一次 wx.config放弃「一次配置全局生效」的假设。另外 access_token 有 7200 秒有效期代码里要做缓存否则频繁刷新 token 会把接口调用配额快速耗尽影响其他业务。4.4 纯积分和积分加现金混合支付别把两套流程塞进同一个下单接口现象纯积分兑换一切正常一旦开启「积分加微信支付」的混合支付下单后经常出现积分扣了但支付没完成订单卡在中间态用户来投诉后台又不敢乱改订单状态。原因混合支付的本质是两个独立动作扣积分是积分系统的事拉起微信支付是支付系统的事。把扣积分塞进支付回调里一旦支付超时或用户中途退出积分状态就悬空了反过来先把积分扣掉再拉起支付用户放弃支付后还得写退款逻辑哪个方向都不好收场。解决下单时先冻结积分并生成待支付订单支付回调里把订单置为已支付再真正扣减冻结积分用户取消支付则解冻退回。纯积分订单走另一条路径校验 → 扣积分 → 直接完成。两条路径可以共用订单表但接口必须分开别用一堆 if 分支把时序搅乱。这里没有后悔药可吃状态机设计错了上线后补数据比写代码痛苦得多。5. 上线前最后一步用一套仿真流程验证积分商城再谈进阶核心链路跑通后我习惯先不急着加功能而是把「一个真实用户从进店到拿货」的完整链路用测试账号过一遍尤其是下面这张验证清单验证项操作预期结果授权闭环未登录用户从分享链接进入自动授权、无感注册、回到原页面积分余额后台给测试号加 500 分商城页显示可用 500 分库存扣减库存 1 的商品并发下两单只有一单成功另一单提示库存不足积分扣减用 300 分兑换一件商品流水表出现一条 -300可用积分变 200取消订单下单后 5 分钟内取消冻结积分退回库存回补 1重复提交快速连点两次兑换按钮只生成一单前端按钮要有防抖这张表跑完说明这套 H5 商城的核心是健康的然后才谈进阶。我比较推荐三个方向一是积分过期任务每天凌晨跑定时脚本把超过有效期的积分按流水扣除注意逐条写流水而不是直接清零否则用户查不到明细又来投诉二是分享加积分在 wx.config 的分享成功回调里给分享者加积分这是拉新成本最低的玩法三是模板消息通知下单成功或发货后用微信模板消息推给用户能明显降低「我换的东西怎么没消息」的客服压力。说一个我自己的习惯每次改动兑换相关逻辑第一件事不是看前端效果而是开两个终端窗口一个 tail 后端日志一个连着数据库盯 points_ledger 和 exchange_order 两张表的变化。积分商城这种系统用户感知到的都是「分没到账」而账务问题的根源几乎都在流水和订单的状态不一致上。这个习惯帮我挡掉了不少上线事故也推荐给你。最后补一句如果你接手积分商城 H5 时能少走两个弯路那这些坑就没白踩。希望帮到你。本文还有配套的精品资源点击获取