generate-minicode.js 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. // Node 脚本:生成携带 storeId 的小程序码(永久码)
  2. // 使用方法:在命令行设置环境变量并运行
  3. // Windows PowerShell:
  4. // $env:WX_APPID="<你的AppID>"; $env:WX_SECRET="<你的AppSecret>"; node server/generate-minicode.js
  5. // 或在同一命令行:
  6. // node server/generate-minicode.js
  7. // 将在 server/output/ 目录下生成 PNG 文件
  8. const https = require('https');
  9. const fs = require('fs');
  10. const path = require('path');
  11. const APPID = process.env.WX_APPID || 'wxbb048b80cd2a14b9';
  12. const SECRET = process.env.WX_SECRET; // 必须提供 AppSecret
  13. const STORE_ID = 'ARhMGM5Y1W';
  14. const PAGE = 'nova-pbf/pages/index/index';
  15. function httpGetJson(url) {
  16. return new Promise((resolve, reject) => {
  17. https
  18. .get(url, (res) => {
  19. let data = '';
  20. res.on('data', (chunk) => (data += chunk));
  21. res.on('end', () => {
  22. try {
  23. const json = JSON.parse(data);
  24. resolve(json);
  25. } catch (e) {
  26. reject(e);
  27. }
  28. });
  29. })
  30. .on('error', reject);
  31. });
  32. }
  33. function httpPostBuffer(url, bodyObj) {
  34. const body = JSON.stringify(bodyObj);
  35. return new Promise((resolve, reject) => {
  36. const req = https.request(url, {
  37. method: 'POST',
  38. headers: {
  39. 'Content-Type': 'application/json',
  40. 'Content-Length': Buffer.byteLength(body),
  41. },
  42. });
  43. const chunks = [];
  44. req.on('response', (res) => {
  45. res.on('data', (chunk) => chunks.push(chunk));
  46. res.on('end', () => resolve(Buffer.concat(chunks)));
  47. });
  48. req.on('error', reject);
  49. req.write(body);
  50. req.end();
  51. });
  52. }
  53. async function main() {
  54. if (!SECRET) {
  55. console.error('❌ 未提供 WX_SECRET(AppSecret)。请设置环境变量后重试。');
  56. process.exit(1);
  57. }
  58. console.log('🔑 获取 access_token...');
  59. const tokenResp = await httpGetJson(
  60. `https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${APPID}&secret=${SECRET}`
  61. );
  62. if (!tokenResp.access_token) {
  63. console.error('❌ 获取 access_token 失败:', tokenResp);
  64. process.exit(1);
  65. }
  66. const accessToken = tokenResp.access_token;
  67. console.log('✅ access_token 获取成功');
  68. console.log('🖨️ 生成携带参数的小程序码...');
  69. const apiUrl = `https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=${accessToken}`;
  70. const payload = {
  71. scene: `storeId=${STORE_ID}`,
  72. page: PAGE,
  73. check_path: false, // 允许未发布页面,仅用于测试/体验
  74. is_hyaline: true,
  75. width: 430,
  76. };
  77. const buf = await httpPostBuffer(apiUrl, payload);
  78. // 微信接口在错误时也可能返回 JSON 文本,需要粗略判断
  79. const isJson = buf.slice(0, 1).toString() === '{';
  80. if (isJson) {
  81. try {
  82. const err = JSON.parse(buf.toString('utf8'));
  83. console.error('❌ 生成小程序码失败:', err);
  84. process.exit(1);
  85. } catch (_) {
  86. // ignore
  87. }
  88. }
  89. const outDir = path.join(__dirname, 'output');
  90. if (!fs.existsSync(outDir)) {
  91. fs.mkdirSync(outDir, { recursive: true });
  92. }
  93. const outPath = path.join(outDir, `minicode_store_${STORE_ID}.png`);
  94. fs.writeFileSync(outPath, buf);
  95. console.log(`✅ 已生成:${outPath}`);
  96. console.log('📌 扫码后将进入页面并携带参数:storeId=', STORE_ID);
  97. }
  98. main().catch((e) => {
  99. console.error('程序异常:', e);
  100. process.exit(1);
  101. });