简介:一份面向移动端微信场景的H5积分兑换商城网站模板,适合需要快速搭建积分体系、签到兑换与代金券营销页面的前端开发者或运营人员。压缩包共112个文件,包含56个png、24个jpg、20个html、9个js、3个css,以页面结构、样式脚本、视觉素材分类组织,整体大小1.19MB,可直接在微信内预览和二次修改。已有623人学习下载。源码覆盖积分获取、每日签到、商品兑换、代金券发放等常见业务模块,并预留微信接口集成位置,便于接入支付、登录与分享。通过调整积分规则和兑换流程,可适配游戏积分商城、会员福利中心等多种运营场景;同时页面素材齐全,适合作为UI参考或功能原型进行快速迭代。对于希望低成本上线积分商城并持续优化用户活跃度的开发团队来说,这套H5源码提供了清晰的基础框架和落地参考。
1. 手机微信里的积分兑换,为什么H5是先跑通业务的最优解
一个功能齐全的H5积分商城,放在微信里跑,最核心的价值不是“炫”,而是用最低的成本把“积分能换东西”这件事闭环。我拆过这类工程,也帮人救过火,最深的体感是:小程序要过审核、要发版、要处理分包,一套流程走下来,活动档期早过了;而H5挂在公众号菜单或二维码里,后端接口就绪,前端改完直接上线,当天就能让用户兑到第一件商品。所以当你手头拿到“手机微信积分兑换商城H5.zip”这类资源,别急着问它够不够“高级”,先问它能不能覆盖商品展示、积分扣减、兑换下单、订单查询这条主链路。能覆盖,就是可用的基座。这篇笔记从工程结构、数据表、核心流程、微信联调再到上线自测,完整拆一遍,适合两类人:一类是刚接手商城类H5、需要快速看懂别人代码的初级开发,另一类是后端为主、被拉来救前端场的全栈。后面所有的代码和配置,我尽量按“常见做法”来讲,你拿到zip后能直接对照着改。
2. 拆开zip包:前端工程结构、数据表设计与本地联调三步
2.1 先看package.json和入口文件,判断技术栈
拿到工程第一步不是看业务代码,而是看依赖和配置文件。大多数H5积分商城会选用Vue或React系框架,配上UI组件库、axios封装和一两个微信SDK。我见过一个比较典型的工程结构:src下按api、assets、components、views、router、store划分,views里有goods、exchange、order、user四个页面文件夹,基本就能对应上积分商城的主流程。打开package.json,重点看三样东西:框架版本、UI库、以及有没有内置微信JS-SDK相关依赖。
{ "dependencies": { "axios": "^1.6.0", "dayjs": "^1.11.0", "vant": "^4.0.0", "vue": "^3.4.0", "vue-router": "^4.0.0", "vuex": "^4.0.0", "weixin-js-sdk": "^1.6.0" } }这段依赖说明它大概率是Vue 3 + Vant的移动端组合。Vant是移动端比较常用的组件库,商品卡片、弹出层、地址编辑都有现成组件;weixin-js-sdk负责微信里的分享、支付、扫一扫等原生能力调用。如果你的zip里没有这个依赖,说明作者可能把SDK直接挂在全局引入,也不影响。接着看src/main.js,确认路由模式和全局拦截器:一般商城类H5用hash路由居多,因为部署在公众号菜单里不需要服务端配history回退;axios拦截器里会统一注入token,并在401时跳转登录页。看完这两个文件,你对这个工程“能不能跑起来”已经有七成把握了。
2.2 数据表设计:积分账户、商品、订单、流水各司其职
后端接口文档不一定随zip附带,但前端代码里会暴露接口路径和参数字段。从这些字段反推表结构,是拆工程最实用的方法。一个功能齐全的积分商城,后端至少要有四张表:会员账户表、积分流水表、商品表、兑换订单表。积分为什么不能直接存在用户表一个字段里?因为用户查余额、对账、回溯每笔积分来源时,没有流水根本说不清。常见的设计是账户表只存当前可用积分和累计积分,流水表每发生一次变动就记一条,加也好扣也好,一进一出都留痕。
| 表名 | 关键字段 | 作用 |
|---|---|---|
| member_account | openid、balance、total_earned | 记录用户当前可用积分 |
| points_log | openid、change_type、change_value、order_no | 积分变动明细,入/出/冻结 |
| exchange_goods | goods_id、name、points_price、stock | 兑换商品信息及库存 |
| exchange_order | order_no、openid、goods_id、status、create_time | 兑换订单及状态流转 |
前端请求“积分商品列表”时,GET请求里带page和category两个参数就够;但请求“立即兑换”时,POST的body里除了goods_id,一般还会有address_id、remark、source。source这个字段容易被忽略,它标记是公众号菜单进来的还是扫码进来的,后端做数据统计时会用到。你拆工程时留意接口封装的函数名,比如getGoodsList、createExchangeOrder,基本能拼出完整的后端语义。
2.3 本地联调:用nginx把三个域名指到本地
H5积分商城在本地跑,最麻烦的不是前端,是微信授权和支付回调必须用公网域名。常见做法是:本地起一个nginx,把api.你的域名.com指到本地的后端端口,把static.你的域名.com指到前端静态资源目录,再让微信后台把JS接口安全域名临时指向这台开发机。这样本地代码里写的接口域名、分享链接域名都能正常访问,唯一需要改的是hosts文件。
server { listen 443 ssl; server_name api.devdomain.com; ssl_certificate /path/to/dev.crt; ssl_certificate_key /path/to/dev.key; location / { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } } server { listen 443 ssl; server_name static.devdomain.com; ssl_certificate /path/to/dev.crt; ssl_certificate_key /path/to/dev.key; root /path/to/dist; index index.html; }这段配置里两个server块分别承担API代理和前端静态文件服务。proxy_pass后面的8080是本地后端服务端口,root指向的是前端打包后的dist目录。为什么非要443?因为微信授权回调只认https,你在本地用http调试授权流程,永远会得到“redirect_uri参数错误”。我第一次做这个坑就是没上https,折腾一下午才发现微信根本不看你的解释,它只按照配置的域名和协议来。联调通了之后,nginx配置不要删,后面每次改前端代码,只需要重新npm run build把产物放到root目录,刷新页面就能看到效果,比开devServer再配代理舒服得多。
3. 把兑换链路写通:授权登录、余额扣减与订单状态的三个关键点
3.1 微信授权登录:code换openid的时序不能倒
H5积分商城依赖微信生态,用户在浏览器里也能打开,但要做兑换、查余额,必须识别用户身份。微信网页授权是这套体系的基础。流程上,前端先跳转微信授权页,用户确认后微信回跳到你配置的回调地址并带上code,后端拿code去微信接口换openid和access_token,然后下发自己的登录态token。
// 前端检测无token时跳转授权 const redirectUri = encodeURIComponent(window.location.origin + '/auth/callback'); window.location.href = 'https://open.weixin.qq.com/connect/oauth2/authorize' + '?appid=' + appId + '&redirect_uri=' + redirectUri + '&response_type=code' + '&scope=snsapi_userinfo' + '&state=from_mall#wechat_redirect';这里scope有两种选择:snsapi_base只能拿到openid,静默授权;snsapi_userinfo会弹出确认框,能拿到头像昵称。积分商城一般用snsapi_base就够,因为业务围绕积分和兑换,用户昵称不是强需求。如果你面对的是一套活动类H5,想要在兑换成功后展示“谁兑换了什么”,才需要升级到snsapi_userinfo。注意授权跳转会丢失当前页面的路由参数,所以在跳转前把原路径存到sessionStorage,回调后再location.replace回去,不然用户兑完一个商品就被丢回首页,体验很糟。
3.2 积分扣减与下单:事务和幂等缺一不可
兑换下单的核心矛盾在于:扣积分和减库存必须要么都成功、要么都失败。常见做法是后端在一个事务里完成余额校验、积分扣减、库存扣减、订单创建。前端要配合做的事有两个:一是提交订单时带上幂等键,二是对按钮做防重复提交。幂等键用order_no或一个随机uuid,后端记录到订单表唯一索引里,用户手抖点两次,第二次请求直接返回第一次的订单结果,而不是再扣一次积分。
@Transactional public ExchangeOrder createExchangeOrder(ExchangeRequest req) { // 1. 校验幂等键,防止重复下单 if (orderMapper.selectByOrderNo(req.getOrderNo()) != null) { return orderMapper.selectByOrderNo(req.getOrderNo()); } // 2. 查账户并校验积分余额,注意乐观锁 MemberAccount account = accountMapper.selectByOpenid(req.getOpenid()); if (account.getBalance() < req.getPointsPrice()) { throw new BizException("积分不足"); } // 3. 扣积分,SQL里带条件更新 int updated = accountMapper.deductBalance(req.getOpenid(), req.getPointsPrice()); if (updated == 0) { throw new BizException("积分扣减失败"); } // 4. 扣库存 int stockUpdated = goodsMapper.deductStock(req.getGoodsId()); if (stockUpdated == 0) { throw new BizException("库存不足"); } // 5. 写流水、建订单 pointsLogMapper.insert(buildLog(req, -req.getPointsPrice())); exchangeOrderMapper.insert(buildOrder(req)); return exchangeOrderMapper.selectByOrderNo(req.getOrderNo()); }这段逻辑里最值得抄的是第3步的“条件更新”:SQL里写成UPDATE member_account SET balance = balance - #{points} WHERE openid = #{openid} AND balance >= #{points},数据库自己帮你做余额校验,比先查后扣更安全。第4步库存扣减同理,stock > 0作为更新条件,更新行数为0就说明卖完了。整个过程包在事务里,任何一个环节抛出异常,前面扣掉的积分和库存都会回滚。前端调用这个接口时,要把后端返回的order_no存起来,后续查订单详情、取消订单、支付都靠它。如果你在工程里看到前端把order_no放进了URL query里,那多半是为了分享给好友代付,属于营销场景的扩展,不用惊讶。
3.3 兑换订单状态机:从待支付到已发货的流转边界
纯积分兑换和“积分+现金”混合支付的订单,状态流转不一样。纯积分兑换在下单时就已经完成扣减,订单直接进入“已兑换”状态,等待发货;混合支付的订单则需要先支付现金部分,再去减积分。前端拿到订单列表后,需要根据状态码渲染不同的操作按钮:待支付显示“去支付”,已发货显示“查看物流”,已完成显示“再次兑换”。状态码后端一般用数字枚举,前端用map映射成文案,两边对应关系要核对清楚。
| 状态码 | 含义 | 前端展示 | 后续动作 |
|---|---|---|---|
| 10 | 待支付 | 待支付 | 支付现金部分,超时自动关闭 |
| 20 | 已兑换/待发货 | 待发货 | 后台人工审核发货 |
| 30 | 已发货 | 查看物流 | 用户确认收货 |
| 40 | 已完成 | 已完成 | 展示评价入口 |
| 50 | 已关闭 | 已关闭 | 积分原路退回 |
积分退回这个点容易遗漏。订单关闭或退款时,后端要把积分加回账户并写一条正向流水,前端在“积分明细”里要能看到“兑换退回 +xx”这样的记录。我拆过的一个项目里,积分退回的逻辑根本没实现,订单关闭了积分也没了,用户客诉量直接翻倍。所以你在看工程代码时,优先搜索refund、cancel、closeOrder这几个方法,确认它们有没有调用积分加回接口。没有的话,这个商城只能算“半成品”,上线前必须补上。
4. 避坑实录:H5积分商城联调微信时最常踩的五个坑
4.1 公众号菜单打开H5,登录态秒失效
现象:用户在微信里从公众号菜单点进商城,刚看到商品列表,点击兑换就跳回授权页,再授权又跳回来,反复横跳。原因多半是前端用localStorage存token,而微信内置浏览器的localStorage在某些机型上会因缓存策略被清理。解决:token改用cookie存储,并设置较长的过期时间,同时在后端接口返回token过期时,前端用静默授权接口续期,而不是直接跳用户授权页。
4.2 余额并发扣减:两个订单同时下单超发
现象:用户账户显示有100积分,同时开了两个商品详情页,快速点击兑换,两个订单都成功了,积分扣成负数。原因是余额校验和扣减之间没有加锁,两个请求同时读到余额100,都通过了校验。解决:在扣减SQL上用balance >= #{points}作为条件,数据库层面保证只有一个请求能更新成功。前端也要做防重复提交,最简单的方法是在提交按钮上锁,请求发出后到响应返回前禁止再次点击。
4.3 分享卡片调不起,JS-SDK签名总报错
现象:点击“分享给好友”,微信提示签名无效,或者分享出去没有缩略图和标题。最常见的原因是后端生成签名时取的URL和前端当前页面URL不一致。微信签名校验严格匹配当前页面地址,而分享回调里的URL、前端请求签名时传的URL、后端实际签名用的URL,三个只要有一个差个参数就失败。解决:前端在每次路由变化后,用window.location.href.split('#')[0]获取完整地址传给后端,后端拿这个地址用SHA1生成签名,注意不要自己拼URL,直接原样使用。
4.4 支付回调拿不到订单号,掉单严重
现象:用户现金支付成功后,订单状态还是“待支付”,用户找客服投诉。原因是支付回调里后端根据out_trade_no查订单,但前端下单时传的订单号和回调里返回的订单号格式不一致,比如一个是16位一个是32位,导致查不到。解决:下单时用同一个函数生成订单号,前后端约定好前缀和长度;回调里查不到订单时不要直接return success,要记录日志并告警,人工介入处理。支付回调的验签也必须做,否则伪造回调能白嫖商品。
4.5 积分扣减和库存扣减不在同一个事务里
现象:用户兑换成功,积分扣了,但订单创建失败,用户积分凭空消失。原因是下单接口分了两个方法,一个扣积分、一个建订单,中间没有事务包裹,或者事务只包裹了第一个方法。解决:把扣积分、扣库存、建订单、写流水放到同一个Service方法里,方法上加@Transactional(rollbackFor = Exception.class)。特别注意事务不生效的坑:同类内部调用、方法被非代理对象调用、异常被catch掉了,都会导致事务悄悄失效。
5. 上线前用一键自测脚本:把兑换、回调、对账压一遍再放量
5.1 自测脚本:模拟完整兑换链路
手点页面验证十遍,不如脚本跑一遍接口。上线前我习惯写一个shell脚本,把授权、查商品、下单、查订单、取消订单这几步通过curl完整走一遍,每次部署后先跑它,通过才敢给业务方发上线通知。下面这段脚本能跑通“查商品→下单→查订单”的主链路,你把它改成实际域名和商品ID就能用。
#!/bin/bash # 积分商城上线自测脚本 BASE_URL="https://api.your-mall.com" TOKEN=$(curl -s "$BASE_URL/auth/mock_login?openid=test001" | jq -r '.data.token') # 1. 拉取商品列表 GOODS_LIST=$(curl -s -H "Authorization: Bearer $TOKEN" "$BASE_URL/goods/list?page=1&pageSize=5") echo "商品列表返回: $(echo $GOODS_LIST | jq '.code')" GOODS_ID=$(echo $GOODS_LIST | jq -r '.data.list[0].goods_id') # 2. 提交兑换订单 ORDER_NO="test-$(date +%s)" ORDER_RESP=$(curl -s -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \ -d "{\"goods_id\":\"$GOODS_ID\",\"order_no\":\"$ORDER_NO\",\"address_id\":\"test_addr\"}" \ "$BASE_URL/exchange/create") echo "下单结果: $ORDER_RESP" ORDER_NO=$(echo $ORDER_RESP | jq -r '.data.order_no') # 3. 查询订单详情 ORDER_INFO=$(curl -s -H "Authorization: Bearer $TOKEN" "$BASE_URL/order/detail?order_no=$ORDER_NO") echo "订单状态: $(echo $ORDER_INFO | jq '.data.status')"第一次跑自测脚本,十有八九会卡在第2步,要么提示签名错误、要么提示积分不足。签名错误看是不是token没传对,积分不足就先调后端mock接口给测试账号充值。脚本里用了一个mock_login接口,目的是跳过微信授权,直接换取测试token,这样能复现所有业务逻辑,又不受微信环境限制。脚本跑通后,再把第2步换成真实的微信授权获取token,整套链路就完整了。
5.2 核对三个对账维度:订单、流水、库存
自测脚本通过不等于上线没问题,还要对账。我一般看三个维度:第一,订单表和积分流水表要能对应上,每笔扣减积分都有订单号;第二,商品库存和订单表里已兑换数量要一致;第三,用户的可用积分余额等于初始积分减去累计兑换积分再加上退回积分。这三个对不上,一定有一环逻辑漏了。
| 对账项 | 核对方式 | 常见异常 |
|---|---|---|
| 积分流水与订单 | 按openid分组汇总积分变动,对比账户余额 | 流水缺失或重复写入 |
| 库存与已兑数量 | 商品表stock + 订单表已支付数量 = 初始库存 | 订单关闭但库存扣减未回滚 |
| 积分退回 | 取消订单后查积分流水,应有正向记录 | 关闭订单未触发退回逻辑 |
5.3 debug开关:给前端留一个后门
H5商城上线后,最怕用户反馈“兑换失败”但你复现不了。我习惯在前端路由里留一个debug模式:连续点击版本号五次,弹出调试面板,显示当前token、openid、接口域名、最后一次请求的响应体。用户把这个截图发过来,比你说一百句“清除缓存试试”都管用。调试面板在正式环境要做权限控制,不然任意用户都能看到别人的token信息,一般用isDebug && isInternalUser两个条件同时满足才显示。
这套自测脚本和debug开关,后来成了我做商城类H5的固定流程。从那以后我每次上线前都强制走一遍:先跑脚本验证接口链路,再对一遍订单流水库存,最后让测试同事用debug面板导出一个完整请求日志,三关全过才敢发上线通知。这个zip里的工程代码不一定完美,但把它当成一个带导航的基座,把上面这些环节补齐,它就能从“能跑”变成“能上线”。希望帮到你。
本文还有配套的精品资源,点击获取