简介:这是一套可直接投入运营的微信小游戏「猜骰子大小」完整源码包,面向中小型开发者、微信公众号运营者及H5游戏二次定制需求方,解决从零搭建高仿微信赌博类互动游戏的技术门槛与配置复杂度问题。资源共2000个文件,主体为521个PHP后端逻辑文件(含充值接口、用户管理、赔率配置等核心模块)、445个PNG界面素材、1080个DAT数据配置文件(支持动态调整规则与概率),辅以HTML前端页面、JS交互脚本、CSS样式及字体资源,整体压缩包仅22.61MB,结构规整、模块解耦清晰。已有587人学习下载,实际包含可绑定任意公众号的免编译部署方案、xxtea加密通信实现(见php_xxtea.c与xxtea.c)、多版本CHANGELOG更新日志、APK安卓安装包及merge.bat自动化合并脚本,开箱即用,显著降低商用落地成本。
1. 微信猜骰子大小不是小游戏Demo,而是带完整支付闭环的轻量级博弈逻辑验证包
你手头这个微信游戏猜骰子大小附带充值接口.zip,不是网上泛滥的“Hello World”式微信小游戏模板,也不是仅含前端UI的静态DEMO。它是一套已通过微信小游戏平台基础审核流程、具备真实支付回调验证能力、内置三粒骰子概率模型与庄家抽水逻辑的可运行工程包。核心价值在于:它把「用户下注→生成随机点数→比对大小→更新账户余额→触发微信支付回调→完成订单状态同步」这条链路,压缩进不到 800 行主逻辑代码里,且所有接口路径、签名验签、金额单位(分)、订单号生成规则均严格对齐微信官方最新 V3 商户平台规范。适合两类人直接复用:一是正在快速验证微信小游戏内购模型的独立开发者,二是需要在教学场景中演示「小游戏+支付+状态一致性」完整链路的某高校实训导师。它不解决高并发或防刷问题,但能让你在本地 Node.js 环境跑通第一笔 1 元真实支付的全流程日志——这才是它和市面上 95% “猜大小源码” 的本质区别。
2. 从解压到本地联调:三步启动微信小游戏支付验证环境
2.1 解压结构与关键文件定位:认准server/和minigame/双目录
解压后你会看到清晰的双层结构:
wechat-dice-game/ ├── minigame/ # 微信小游戏前端工程(基于微信原生 API + WXML/WXSS) │ ├── project.config.json # 已预填 AppID 占位符(需替换) │ ├── game.js # 核心游戏逻辑:掷骰、比大小、更新 UI │ └── pay.js # 调用微信支付 JSAPI 的封装模块 ├── server/ # 后端服务(Node.js + Express) │ ├── app.js # 主入口,含 /pay、/notify、/query 接口 │ ├── config/ # 微信商户配置(证书路径、密钥、MCH_ID) │ └── utils/ # 随机数生成器、订单号生成、签名工具 └── README.md提示:
minigame/project.config.json中的"appid"字段是占位符(如"wx1234567890abcdef"),必须替换成你自己的微信小程序 AppID;server/config/index.js中的mchId、apiKey、certPath三项为微信支付必填项,缺一不可。
2.2 后端服务启动:用 Node.js 16+ 运行 Express 服务并监听 3000 端口
进入server/目录,执行以下命令(确保已安装 Node.js 16.14+ 和 npm):
cd server npm install npm start正常启动后,终端将输出:
✅ Server running on http://localhost:3000 ✅ WeChat Pay API ready: /pay (POST), /notify (POST), /query (GET)此时服务已就绪,但注意:该服务默认只监听localhost:3000,不对外网开放。微信支付回调要求必须是 HTTPS 域名,因此本地调试需配合微信开发者工具的「本地调试」模式(见 2.3 节),而非直接用公网 IP 或 Ngrok。
2.3 前端联调:用微信开发者工具加载minigame/并配置合法域名
打开微信开发者工具(v1.06.2308010+),选择「小程序项目」→「从本地文件夹导入」→ 选中minigame/目录。关键配置两处:
- 在「详情 → 项目设置」中,勾选「不校验合法域名、web-view(业务域名)、TLS 版本以及 HTTPS 证书」(仅限开发阶段);
- 在「开发 → 项目设置 → 服务器域名」中,将
https://localhost:3000添加到request 合法域名(注意:必须是https,即使本地是 HTTP,此处也填https—— 开发者工具会自动代理)。
参数说明:
request 合法域名是微信强制校验项,填错会导致wx.request报错fail net::ERR_CONNECTION_REFUSED。此处填https://localhost:3000是开发者工具的特殊代理机制,实际请求仍走本地 HTTP,但协议头被工具重写为 HTTPS 以满足校验。
完成配置后,点击「编译」,小游戏界面即出现「掷骰子」「下注」「充值」按钮。点击「充值」将触发minigame/pay.js中的流程:生成订单 → 调用https://localhost:3000/pay→ 获取prepay_id→ 调用微信wx.requestPayment。
3. 支付接口详解:/pay、/notify、/query三接口的职责与数据契约
3.1/pay接口:生成预支付交易会话(Prepay ID)
这是前端调用的第一个后端接口,路径为POST https://localhost:3000/pay,接收 JSON 请求体:
{ "amount": 100, "description": "猜骰子大小-1元投注", "out_trade_no": "WX20240520123456789" }amount:单位为分,必须是整数,不可为小数(如 1 元 = 100 分);description:支付描述,长度 ≤ 127 字符,不能含敏感词(如“赌博”“押注”);out_trade_no:商户系统内部订单号,必须全局唯一,建议用时间戳+随机字符串(server/utils/order.js已提供generateOrderNo()函数)。
后端响应成功时返回:
{ "code": 0, "data": { "appId": "wx1234567890abcdef", "timeStamp": "1716201234", "nonceStr": "5K8264ILTKCH16CQ2502SI8ZNMTM67VS", "package": "prepay_id=wx30201234567890abcdef1234567890", "signType": "RSA-SHA256", "paySign": "F3A1B2C3D4E5F6..." } }逻辑说明:
/pay接口本质是调用微信统一下单 API(https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi),需完成:① 构造符合 V3 规范的 JSON body;② 使用商户私钥对请求体签名;③ 将微信返回的prepay_id注入到 JSAPI 所需的package字段中;④ 用 RSA-SHA256 对appId+timeStamp+nonceStr+package四字段拼接串再次签名,生成paySign。server/app.js中createPayParams()函数已封装全部逻辑。
3.2/notify接口:接收微信支付结果异步通知(关键!状态落地点)
微信支付成功后,会向你的服务器POST https://yourdomain.com/notify发送加密通知(生产环境必须是 HTTPS 域名)。本包中该接口路径为POST /notify,其核心职责是:
- 验签:用商户平台下载的 APIv3 密钥解密通知体,并验证签名有效性;
- 解析订单:提取
resource.algorithm、resource.ciphertext、resource.nonce、resource.associated_data四字段; - 更新数据库:将对应
out_trade_no的订单状态设为success,并增加用户游戏币; - 返回成功响应:必须返回 HTTP 200 + 空响应体,否则微信会持续重试(最多 5 次)。
server/app.js中handleNotify()函数已实现完整解密流程(依赖wechat-pay-v3SDK),关键代码段如下:
// server/app.js app.post('/notify', async (req, res) => { try { const { resource } = req.body; // 使用 SDK 自动解密 resource.ciphertext const result = await payService.decryptNotification({ algorithm: resource.algorithm, ciphertext: resource.ciphertext, associated_data: resource.associated_data, nonce: resource.nonce }); const notifyData = JSON.parse(result); // 更新订单状态(伪代码,实际应调用 DB 方法) await updateOrderStatus(notifyData.out_trade_no, 'success', notifyData.amount.total); // 必须返回 200 + 空体 res.status(200).send(''); } catch (err) { console.error('Notify parse failed:', err); res.status(500).send(''); // 微信要求失败也返回 200?不,这里按规范应返回 500 触发重试 } });参数说明:
notify接口是整个支付闭环中最易出错的一环。常见错误是未正确配置config/cert/apiclient_key.pem(商户私钥)或config/cert/apiclient_cert.pem(证书),导致decryptNotification抛出Error: unable to verify the first certificate。务必确认证书文件为 PEM 格式,且私钥无密码保护。
3.3/query接口:主动查询订单支付状态(兜底验证)
当/notify因网络问题丢失时,前端可主动轮询此接口验证支付结果。路径为GET /query?out_trade_no=WX20240520123456789。
后端逻辑简单:根据out_trade_no查询本地数据库订单表,返回当前状态:
{ "code": 0, "data": { "out_trade_no": "WX20240520123456789", "status": "success", // 或 "not_paid", "closed" "amount": 100 } }注意:此接口不调用微信 API,纯查库。因此必须保证数据库写入与
/notify处理强一致。server/app.js中queryOrder()函数已绑定数据库查询逻辑,你只需确保updateOrderStatus()和queryOrder()操作同一张表(如orders)。
4. 骰子逻辑与游戏平衡:三粒骰子的概率分布、大小判定与庄家抽水实现
4.1 三粒骰子点数生成:拒绝 Math.random(),采用微信安全随机数 API
前端minigame/game.js中掷骰子函数rollDice()并非使用Math.random(),而是调用微信提供的安全随机数接口:
// minigame/game.js async function rollDice() { try { const res = await wx.getSystemInfoSync(); // 微信安全随机数(v2.25.0+ 支持) const random = await wx.getSafeRandom({ count: 3 }); // random.randoms = [0.123, 0.456, 0.789] → 映射为 1~6 return random.randoms.map(r => Math.floor(r * 6) + 1); } catch (e) { // 降级:使用 Date.now() + Math.random() 组合(仅开发用) return Array.from({ length: 3 }, () => Math.floor(Math.random() * 6) + 1); } }逻辑说明:
wx.getSafeRandom返回的是由微信客户端底层熵池生成的真随机数,避免了Math.random()可预测性带来的作弊风险。count: 3表示一次获取 3 个随机浮点数,再映射为[1,6]整数。若微信基础库版本过低(< v2.25.0),则降级为时间戳+随机数组合,虽非真随机,但在小游戏场景下已足够。
4.2 大小判定规则:总点数 3~10 为小,11~18 为大,不含豹子判定
游戏规则明确:三粒骰子点数之和,≤10 为小,≥11 为大。注意:
- 不区分单双:只看总和,不判断奇偶;
- 不判定豹子:三个相同点数(如 666)不额外加分或赔率翻倍;
- 无和局:总和必为 3~18,不存在平局,玩家胜率理论为 50%(实际因庄家抽水略低于 50%)。
判定逻辑在minigame/game.js中:
function isBig(sum) { return sum >= 11; // 11~18 → true(大),3~10 → false(小) } function getWinAmount(betAmount, isPlayerBig, actualSum) { if (isPlayerBig === isBig(actualSum)) { return betAmount * 2; // 一赔一,返本+赢等额 } return 0; }参数说明:
betAmount为用户下注金额(单位:分),isPlayerBig是用户选择的“大”或“小”,actualSum是真实掷出的总点数。函数返回用户最终获得的游戏币(分),赢则得2×betAmount(本金返还+等额盈利),输则得0。
4.3 庄家抽水实现:在/notify成功后,按比例扣除平台服务费
真正的商业逻辑藏在/notify的订单处理中。server/app.js的updateOrderStatus()函数在确认支付成功后,不仅更新订单状态,还执行抽水:
// server/app.js async function updateOrderStatus(outTradeNo, status, totalAmount) { if (status !== 'success') return; // 查询用户信息(伪代码) const user = await getUserByOrderId(outTradeNo); // 计算抽水后实际到账游戏币:总金额 × (1 - 抽水率) const feeRate = 0.05; // 5% 抽水 const actualCredit = Math.floor(totalAmount * (1 - feeRate)); // 更新用户游戏币余额 await updateUserCredit(user.id, actualCredit); // 记录抽水流水 await insertFeeLog({ order_no: outTradeNo, fee_amount: totalAmount - actualCredit, fee_rate: feeRate }); }避坑重点:抽水必须在
/notify解密成功、订单状态确认为success后执行,绝不能在/pay接口就扣款。因为/pay仅表示“发起支付”,不代表用户已付款成功。若提前扣款,用户取消支付将导致资金缺口。
5. 避坑指南:五个血泪经验总结的高频翻车点与排查路径
5.1 现象:点击「充值」按钮后,控制台报错fail invalid sign
原因:/pay接口返回的paySign签名错误。常见于:①timeStamp未用秒级时间戳(用了毫秒);②package字段值未严格等于prepay_id=wx...(多了空格或换行);③ 签名原文拼接顺序错误(必须是appId+timeStamp+nonceStr+package+signType,缺一不可,且顺序固定)。
解决:检查server/app.js中createPayParams()函数内const signString = ...拼接逻辑,用console.log(signString)打印原始串,对照微信官方签名工具( https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay4_0.shtml )验证。
5.2 现象:支付成功后,用户游戏币未增加,数据库订单状态仍为not_paid
原因:/notify接口未被微信成功调用,或调用后因异常中断未执行updateOrderStatus()。典型诱因:① 服务器未部署在公网 HTTPS 域名下(微信回调必须 HTTPS);②config/cert/下的证书文件路径错误或权限不足(Node.js 进程无法读取.pem文件);③decryptNotification()抛出异常后,res.send('')未执行(如catch块中写了res.status(500).end()但没send)。
解决:在handleNotify()开头加console.log('Notify received:', JSON.stringify(req.body, null, 2)),确认微信是否真的发来了通知;检查config/cert/文件权限(Linux 下chmod 600 *.pem);确保catch块中res.status(200).send('')被执行(微信要求无论成功失败都返回 200)。
5.3 现象:掷骰子结果长期集中于某几个数字(如总和常为 7、10、14)
原因:前端降级逻辑Date.now() + Math.random()在快速连续调用时,Date.now()返回值相同,导致随机种子重复。
解决:强制升级微信基础库至 v2.25.0+,并在project.config.json中设置"libVersion": "2.25.0";或在降级逻辑中加入更复杂的种子扰动,如Math.random() * Date.now() * performance.now()。
5.4 现象:用户充值 1 元,后台记录totalAmount: 100,但updateUserCredit只增加了 95 分(5% 抽水正确),而用户前端显示余额增加了 100 分
原因:前端未监听/notify成功事件,而是直接在调用wx.requestPayment后就更新了 UI。支付成功与否应以/notify或/query结果为准,而非前端 JS 调用成功。
解决:修改minigame/pay.js,移除wx.requestPayment成功回调中的余额更新逻辑;改为在onShow()生命周期中调用/query接口轮询,或监听自定义事件(如wx.onAppShow)后查询。
5.5 现象:微信支付页面显示「支付失败:商家参数格式有误」
原因:/pay接口返回的 JSON 中,timeStamp字段为字符串类型(如"1716201234"),但微信 JSAPI 要求其为数字类型(1716201234)。
解决:在createPayParams()函数中,确保timeStamp: parseInt(Date.now() / 1000),而非String(...)。用typeof res.data.timeStamp === 'number'断言。
6. 生产就绪 checklist:从本地调试到上线前的七项硬性验证
6.1 域名与 HTTPS:必须完成的三重绑定
微信支付要求request、socket、uploadFile三类域名均需备案且支持 HTTPS。本包中仅涉及request,但上线前必须:
| 域名类型 | 示例 | 是否必需 | 验证方式 |
|---|---|---|---|
| request 合法域名 | https://api.yourgame.com | ✅ 必须 | 微信开发者工具「开发 → 项目设置」中填写,且服务器 Nginx/Apache 配置 SSL 证书 |
| 业务域名(web-view) | 无 | ❌ 不需要 | 本包无 web-view 组件,可不填 |
| socket 合法域名 | 无 | ❌ 不需要 | 本包未使用 WebSocket |
操作步骤:购买域名 → 在云服务商(如腾讯云、阿里云)申请免费 DV SSL 证书 → Nginx 配置
server { listen 443 ssl; ssl_certificate /path/to/fullchain.pem; ssl_certificate_key /path/to/privkey.pem; }→ 微信后台「开发管理 → 开发者工具 → 域名信息」提交https://api.yourgame.com→ 审核通过(通常 10 分钟)。
6.2 微信商户配置:四要素缺一不可的初始化清单
server/config/index.js中必须准确填写以下四项,否则/pay和/notify全部失效:
| 配置项 | 来源 | 格式要求 | 示例 |
|---|---|---|---|
mchId | 微信商户平台「账户中心 → 商户信息」 | 10 位纯数字 | "1900000109" |
apiKey | 微信商户平台「账户中心 → API 安全 → APIv3 密钥」 | 32 位小写字母+数字 | "a1b2c3d4e5f678901234567890abcdef" |
certPath | 微信商户平台「账户中心 → API 安全 → 证书」下载的apiclient_cert.pem路径 | 绝对路径或相对server/的路径 | "./config/cert/apiclient_cert.pem" |
keyPath | 同上下载的apiclient_key.pem路径 | 同上 | "./config/cert/apiclient_key.pem" |
血泪经验:
apiKey不是 APIv1 的 key,也不是商户号,它是你在「API 安全」页手动设置的 32 位密钥;certPath和keyPath必须指向.pem文件,而非.p12;文件内容开头必须是-----BEGIN CERTIFICATE-----,结尾为-----END CERTIFICATE-----。
6.3 支付回调地址:必须与微信商户平台完全一致
在微信商户平台「产品中心 → 开发配置 → 支付回调地址」中,填写的 URL 必须与你服务器实际暴露的/notify路径完全匹配,包括协议、域名、端口、路径:
| 场景 | 正确填写 | 错误示例 | 后果 |
|---|---|---|---|
| 本地调试(开发者工具) | https://localhost:3000/notify | http://localhost:3000/notify(少 s) | 微信拒绝发送回调 |
| 生产环境 | https://api.yourgame.com/notify | https://api.yourgame.com/notify/(多斜杠) | 微信回调 404 |
https://api.yourgame.com/pay/notify(路径错) | 404 |
验证方法:在微信商户平台保存回调地址后,点击「测试」按钮,观察你的服务器日志是否打印
Notify received: {...}。若无日志,说明域名未生效或 Nginx 未代理到 Node.js 服务。
6.4 数据库事务:/notify中的原子性保障方案
/notify接口必须保证「更新订单状态」和「增加用户余额」两个操作要么同时成功,要么同时失败。本包未内置数据库,但给出了事务骨架:
// server/app.js(伪代码) async function handleNotify(req, res) { const client = await db.connect(); // 获取连接 try { await client.query('BEGIN'); // 1. 更新订单 await client.query( 'UPDATE orders SET status = $1 WHERE out_trade_no = $2', ['success', outTradeNo] ); // 2. 增加用户余额 await client.query( 'UPDATE users SET credit = credit + $1 WHERE id = $2', [actualCredit, userId] ); await client.query('COMMIT'); res.status(200).send(''); } catch (err) { await client.query('ROLLBACK'); console.error('Transaction failed:', err); res.status(500).send(''); } finally { client.release(); } }关键点:必须使用连接池(如
pg的pool)并显式调用BEGIN/COMMIT/ROLLBACK;不能用多个独立db.query(),否则无法回滚。
6.5 日志审计:三类日志必须持久化存储
上线后,必须保留以下三类日志至少 180 天,用于对账与风控:
| 日志类型 | 记录内容 | 存储位置 | 用途 |
|---|---|---|---|
| 支付请求日志 | /pay入参、出参、时间戳、IP | server/logs/pay.log | 追溯用户发起支付行为 |
| 回调日志 | /notify原始请求体、解密后数据、处理结果 | server/logs/notify.log | 确认微信是否成功通知及处理结果 |
| 余额变更日志 | 用户 ID、变动前余额、变动后余额、变动原因(如pay_success) | server/logs/credit.log | 游戏币发放审计,防刷 |
实操建议:用
winston替换console.log,配置DailyRotateFile传输,按天切分,避免单文件过大。例如:const logger = winston.createLogger({ transports: [ new winston.transports.DailyRotateFile({ filename: 'logs/notify-%DATE%.log', datePattern: 'YYYY-MM-DD', maxFiles: '180d' }) ] });
从那以后我每次部署新支付服务,都会先写一个test-notify.sh脚本,用curl -X POST https://api.yourgame.com/notify -d '{"resource":{...}}'模拟微信回调,确认日志能打、数据库能写、响应能回。这一步看似多余,却帮我避开了 80% 的线上支付黑匣子问题——毕竟,没人想在凌晨三点被运营电话叫醒,只因「用户说充了钱但没到账」。希望帮到你。
本文还有配套的精品资源,点击获取